1 /*
2  * Copyright © 2009 CNRS
3  * Copyright © 2009-2020 Inria.  All rights reserved.
4  * Copyright © 2009-2011 Université Bordeaux
5  * Copyright © 2009-2011 Cisco Systems, Inc.  All rights reserved.
6  * See COPYING in top-level directory.
7  */
8 
9 #include "private/autogen/config.h"
10 #include "hwloc.h"
11 #include "hwloc/plugins.h"
12 #include "private/private.h"
13 #include "private/misc.h"
14 #include "private/xml.h"
15 #include "private/debug.h"
16 
17 #include <string.h>
18 #include <assert.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #ifdef HAVE_UNISTD_H
22 #include <unistd.h>
23 #endif
24 
25 /*******************
26  * Import routines *
27  *******************/
28 
29 struct hwloc__nolibxml_backend_data_s {
30   size_t buflen; /* size of both buffer, set during backend_init() */
31   char *buffer; /* allocated and filled during backend_init() */
32 };
33 
34 typedef struct hwloc__nolibxml_import_state_data_s {
35   char *tagbuffer; /* buffer containing the next tag */
36   char *attrbuffer; /* buffer containing the next attribute of the current node */
37   const char *tagname; /* tag name of the current node */
38   int closed; /* set if the current node is auto-closing */
39 } __hwloc_attribute_may_alias * hwloc__nolibxml_import_state_data_t;
40 
41 static char *
hwloc__nolibxml_import_ignore_spaces(char * buffer)42 hwloc__nolibxml_import_ignore_spaces(char *buffer)
43 {
44   return buffer + strspn(buffer, " \t\n");
45 }
46 
47 static int
hwloc__nolibxml_import_next_attr(hwloc__xml_import_state_t state,char ** namep,char ** valuep)48 hwloc__nolibxml_import_next_attr(hwloc__xml_import_state_t state, char **namep, char **valuep)
49 {
50   hwloc__nolibxml_import_state_data_t nstate = (void*) state->data;
51   size_t namelen;
52   size_t len, escaped;
53   char *buffer, *value, *end;
54 
55   if (!nstate->attrbuffer)
56     return -1;
57 
58   /* find the beginning of an attribute */
59   buffer = hwloc__nolibxml_import_ignore_spaces(nstate->attrbuffer);
60   namelen = strspn(buffer, "abcdefghijklmnopqrstuvwxyz_");
61   if (buffer[namelen] != '=' || buffer[namelen+1] != '\"')
62     return -1;
63   buffer[namelen] = '\0';
64   *namep = buffer;
65 
66   /* find the beginning of its value, and unescape it */
67   *valuep = value = buffer+namelen+2;
68   len = 0; escaped = 0;
69   while (value[len+escaped] != '\"') {
70     if (value[len+escaped] == '&') {
71       if (!strncmp(&value[1+len+escaped], "#10;", 4)) {
72 	escaped += 4;
73 	value[len] = '\n';
74       } else if (!strncmp(&value[1+len+escaped], "#13;", 4)) {
75 	escaped += 4;
76 	value[len] = '\r';
77       } else if (!strncmp(&value[1+len+escaped], "#9;", 3)) {
78 	escaped += 3;
79 	value[len] = '\t';
80       } else if (!strncmp(&value[1+len+escaped], "quot;", 5)) {
81 	escaped += 5;
82 	value[len] = '\"';
83       } else if (!strncmp(&value[1+len+escaped], "lt;", 3)) {
84 	escaped += 3;
85 	value[len] = '<';
86       } else if (!strncmp(&value[1+len+escaped], "gt;", 3)) {
87 	escaped += 3;
88 	value[len] = '>';
89       } else if (!strncmp(&value[1+len+escaped], "amp;", 4)) {
90 	escaped += 4;
91 	value[len] = '&';
92       } else {
93 	return -1;
94       }
95     } else {
96       value[len] = value[len+escaped];
97     }
98     len++;
99     if (value[len+escaped] == '\0')
100       return -1;
101   }
102   value[len] = '\0';
103 
104   /* find next attribute */
105   end = &value[len+escaped+1]; /* skip the ending " */
106   nstate->attrbuffer = hwloc__nolibxml_import_ignore_spaces(end);
107   return 0;
108 }
109 
110 static int
hwloc__nolibxml_import_find_child(hwloc__xml_import_state_t state,hwloc__xml_import_state_t childstate,char ** tagp)111 hwloc__nolibxml_import_find_child(hwloc__xml_import_state_t state,
112 				  hwloc__xml_import_state_t childstate,
113 				  char **tagp)
114 {
115   hwloc__nolibxml_import_state_data_t nstate = (void*) state->data;
116   hwloc__nolibxml_import_state_data_t nchildstate = (void*) childstate->data;
117   char *buffer = nstate->tagbuffer;
118   char *end;
119   char *tag;
120   size_t namelen;
121 
122   childstate->parent = state;
123   childstate->global = state->global;
124 
125   /* auto-closed tags have no children */
126   if (nstate->closed)
127     return 0;
128 
129   /* find the beginning of the tag */
130   buffer = hwloc__nolibxml_import_ignore_spaces(buffer);
131   if (buffer[0] != '<')
132     return -1;
133   buffer++;
134 
135   /* if closing tag, return nothing and do not advance */
136   if (buffer[0] == '/')
137     return 0;
138 
139   /* normal tag */
140   nchildstate->tagname = tag = buffer;
141 
142   /* find the end, mark it and return it */
143   end = strchr(buffer, '>');
144   if (!end)
145     return -1;
146   end[0] = '\0';
147   nchildstate->tagbuffer = end+1;
148 
149   /* handle auto-closing tags */
150   if (end[-1] == '/') {
151     nchildstate->closed = 1;
152     end[-1] = '\0';
153   } else
154     nchildstate->closed = 0;
155 
156   /* find attributes */
157   namelen = strspn(buffer, "abcdefghijklmnopqrstuvwxyz1234567890_");
158 
159   if (buffer[namelen] == '\0') {
160     /* no attributes */
161     nchildstate->attrbuffer = NULL;
162     *tagp = tag;
163     return 1;
164   }
165 
166   if (buffer[namelen] != ' ')
167     return -1;
168 
169   /* found a space, likely starting attributes */
170   buffer[namelen] = '\0';
171   nchildstate->attrbuffer = buffer+namelen+1;
172   *tagp = tag;
173   return 1;
174 }
175 
176 static int
hwloc__nolibxml_import_close_tag(hwloc__xml_import_state_t state)177 hwloc__nolibxml_import_close_tag(hwloc__xml_import_state_t state)
178 {
179   hwloc__nolibxml_import_state_data_t nstate = (void*) state->data;
180   char *buffer = nstate->tagbuffer;
181   char *end;
182 
183   /* auto-closed tags need nothing */
184   if (nstate->closed)
185     return 0;
186 
187   /* find the beginning of the tag */
188   buffer = hwloc__nolibxml_import_ignore_spaces(buffer);
189   if (buffer[0] != '<')
190     return -1;
191   buffer++;
192 
193   /* find the end, mark it and return it to the parent */
194   end = strchr(buffer, '>');
195   if (!end)
196     return -1;
197   end[0] = '\0';
198   nstate->tagbuffer = end+1;
199 
200   /* if closing tag, return nothing */
201   if (buffer[0] != '/' || strcmp(buffer+1, nstate->tagname) )
202     return -1;
203   return 0;
204 }
205 
206 static void
hwloc__nolibxml_import_close_child(hwloc__xml_import_state_t state)207 hwloc__nolibxml_import_close_child(hwloc__xml_import_state_t state)
208 {
209   hwloc__nolibxml_import_state_data_t nstate = (void*) state->data;
210   hwloc__nolibxml_import_state_data_t nparent = (void*) state->parent->data;
211   nparent->tagbuffer = nstate->tagbuffer;
212 }
213 
214 static int
hwloc__nolibxml_import_get_content(hwloc__xml_import_state_t state,const char ** beginp,size_t expected_length)215 hwloc__nolibxml_import_get_content(hwloc__xml_import_state_t state,
216 				   const char **beginp, size_t expected_length)
217 {
218   hwloc__nolibxml_import_state_data_t nstate = (void*) state->data;
219   char *buffer = nstate->tagbuffer;
220   size_t length;
221   char *end;
222 
223   /* auto-closed tags have no content */
224   if (nstate->closed) {
225     if (expected_length)
226       return -1;
227     *beginp = "";
228     return 0;
229   }
230 
231   /* find the next tag, where the content ends */
232   end = strchr(buffer, '<');
233   if (!end)
234     return -1;
235 
236   length = (size_t) (end-buffer);
237   if (length != expected_length)
238     return -1;
239   nstate->tagbuffer = end;
240   *end = '\0'; /* mark as 0-terminated for now */
241   *beginp = buffer;
242   return 1;
243 }
244 
245 static void
hwloc__nolibxml_import_close_content(hwloc__xml_import_state_t state)246 hwloc__nolibxml_import_close_content(hwloc__xml_import_state_t state)
247 {
248   /* put back the '<' that we overwrote to 0-terminate the content */
249   hwloc__nolibxml_import_state_data_t nstate = (void*) state->data;
250   if (!nstate->closed)
251     *nstate->tagbuffer = '<';
252 }
253 
254 static int
hwloc_nolibxml_look_init(struct hwloc_xml_backend_data_s * bdata,struct hwloc__xml_import_state_s * state)255 hwloc_nolibxml_look_init(struct hwloc_xml_backend_data_s *bdata,
256 			 struct hwloc__xml_import_state_s *state)
257 {
258   hwloc__nolibxml_import_state_data_t nstate = (void*) state->data;
259   struct hwloc__nolibxml_backend_data_s *nbdata = bdata->data;
260   unsigned major, minor;
261   char *end;
262   char *buffer = nbdata->buffer;
263   const char *tagname;
264 
265   HWLOC_BUILD_ASSERT(sizeof(*nstate) <= sizeof(state->data));
266 
267   /* skip headers */
268   while (!strncmp(buffer, "<?xml ", 6) || !strncmp(buffer, "<!DOCTYPE ", 10)) {
269     buffer = strchr(buffer, '\n');
270     if (!buffer)
271       goto failed;
272     buffer++;
273   }
274 
275   /* find topology tag */
276   if (sscanf(buffer, "<topology version=\"%u.%u\">", &major, &minor) == 2) {
277     bdata->version_major = major;
278     bdata->version_minor = minor;
279     end = strchr(buffer, '>') + 1;
280     tagname = "topology";
281   } else if (!strncmp(buffer, "<topology>", 10)) {
282     bdata->version_major = 1;
283     bdata->version_minor = 0;
284     end = buffer + 10;
285     tagname = "topology";
286   } else if (!strncmp(buffer, "<root>", 6)) {
287     bdata->version_major = 0;
288     bdata->version_minor = 9;
289     end = buffer + 6;
290     tagname = "root";
291   } else
292     goto failed;
293 
294   state->global->next_attr = hwloc__nolibxml_import_next_attr;
295   state->global->find_child = hwloc__nolibxml_import_find_child;
296   state->global->close_tag = hwloc__nolibxml_import_close_tag;
297   state->global->close_child = hwloc__nolibxml_import_close_child;
298   state->global->get_content = hwloc__nolibxml_import_get_content;
299   state->global->close_content = hwloc__nolibxml_import_close_content;
300   state->parent = NULL;
301   nstate->closed = 0;
302   nstate->tagbuffer = end;
303   nstate->tagname = tagname;
304   nstate->attrbuffer = NULL;
305   return 0; /* success */
306 
307  failed:
308   return -1; /* failed */
309 }
310 
311 /* can be called at the end of the import (to cleanup things early),
312  * or by backend_exit() if load failed for other reasons.
313  */
314 static void
hwloc_nolibxml_free_buffers(struct hwloc_xml_backend_data_s * bdata)315 hwloc_nolibxml_free_buffers(struct hwloc_xml_backend_data_s *bdata)
316 {
317   struct hwloc__nolibxml_backend_data_s *nbdata = bdata->data;
318   if (nbdata->buffer) {
319     free(nbdata->buffer);
320     nbdata->buffer = NULL;
321   }
322 }
323 
324 static void
hwloc_nolibxml_look_done(struct hwloc_xml_backend_data_s * bdata,int result)325 hwloc_nolibxml_look_done(struct hwloc_xml_backend_data_s *bdata, int result)
326 {
327   hwloc_nolibxml_free_buffers(bdata);
328 
329   if (result < 0 && hwloc__xml_verbose())
330     fprintf(stderr, "Failed to parse XML input with the minimalistic parser. If it was not\n"
331 	    "generated by hwloc, try enabling full XML support with libxml2.\n");
332 }
333 
334 /********************
335  * Backend routines *
336  ********************/
337 
338 static void
hwloc_nolibxml_backend_exit(struct hwloc_xml_backend_data_s * bdata)339 hwloc_nolibxml_backend_exit(struct hwloc_xml_backend_data_s *bdata)
340 {
341   struct hwloc__nolibxml_backend_data_s *nbdata = bdata->data;
342   hwloc_nolibxml_free_buffers(bdata);
343   free(nbdata);
344 }
345 
346 static int
hwloc_nolibxml_read_file(const char * xmlpath,char ** bufferp,size_t * buflenp)347 hwloc_nolibxml_read_file(const char *xmlpath, char **bufferp, size_t *buflenp)
348 {
349   FILE * file;
350   size_t buflen, offset, readlen;
351   struct stat statbuf;
352   char *buffer, *tmp;
353   size_t ret;
354 
355   if (!strcmp(xmlpath, "-"))
356     xmlpath = "/dev/stdin";
357 
358   file = fopen(xmlpath, "r");
359   if (!file)
360     goto out;
361 
362   /* find the required buffer size for regular files, or use 4k when unknown, we'll realloc later if needed */
363   buflen = 4096;
364   if (!stat(xmlpath, &statbuf))
365     if (S_ISREG(statbuf.st_mode))
366       buflen = statbuf.st_size+1; /* one additional byte so that the first fread() gets EOF too */
367 
368   buffer = malloc(buflen+1); /* one more byte for the ending \0 */
369   if (!buffer)
370     goto out_with_file;
371 
372   offset = 0; readlen = buflen;
373   while (1) {
374     ret = fread(buffer+offset, 1, readlen, file);
375 
376     offset += ret;
377     buffer[offset] = 0;
378 
379     if (ret != readlen)
380       break;
381 
382     buflen *= 2;
383     tmp = realloc(buffer, buflen+1);
384     if (!tmp)
385       goto out_with_buffer;
386     buffer = tmp;
387     readlen = buflen/2;
388   }
389 
390   fclose(file);
391   *bufferp = buffer;
392   *buflenp = offset+1;
393   return 0;
394 
395  out_with_buffer:
396   free(buffer);
397  out_with_file:
398   fclose(file);
399  out:
400   return -1;
401 }
402 
403 static int
hwloc_nolibxml_backend_init(struct hwloc_xml_backend_data_s * bdata,const char * xmlpath,const char * xmlbuffer,int xmlbuflen)404 hwloc_nolibxml_backend_init(struct hwloc_xml_backend_data_s *bdata,
405 			    const char *xmlpath, const char *xmlbuffer, int xmlbuflen)
406 {
407   struct hwloc__nolibxml_backend_data_s *nbdata = malloc(sizeof(*nbdata));
408 
409   if (!nbdata)
410     goto out;
411   bdata->data = nbdata;
412 
413   if (xmlbuffer) {
414     nbdata->buffer = malloc(xmlbuflen+1);
415     if (!nbdata->buffer)
416       goto out_with_nbdata;
417     nbdata->buflen = xmlbuflen+1;
418     memcpy(nbdata->buffer, xmlbuffer, xmlbuflen);
419     nbdata->buffer[xmlbuflen] = '\0';
420 
421   } else {
422     int err = hwloc_nolibxml_read_file(xmlpath, &nbdata->buffer, &nbdata->buflen);
423     if (err < 0)
424       goto out_with_nbdata;
425   }
426 
427   bdata->look_init = hwloc_nolibxml_look_init;
428   bdata->look_done = hwloc_nolibxml_look_done;
429   bdata->backend_exit = hwloc_nolibxml_backend_exit;
430   return 0;
431 
432 out_with_nbdata:
433   free(nbdata);
434 out:
435   return -1;
436 }
437 
438 static int
hwloc_nolibxml_import_diff(struct hwloc__xml_import_state_s * state,const char * xmlpath,const char * xmlbuffer,int xmlbuflen,hwloc_topology_diff_t * firstdiffp,char ** refnamep)439 hwloc_nolibxml_import_diff(struct hwloc__xml_import_state_s *state,
440 			   const char *xmlpath, const char *xmlbuffer, int xmlbuflen,
441 			   hwloc_topology_diff_t *firstdiffp, char **refnamep)
442 {
443   hwloc__nolibxml_import_state_data_t nstate = (void*) state->data;
444   struct hwloc__xml_import_state_s childstate;
445   char *refname = NULL;
446   char *buffer, *tmp, *tag;
447   size_t buflen;
448   int ret;
449 
450   HWLOC_BUILD_ASSERT(sizeof(*nstate) <= sizeof(state->data));
451 
452   if (xmlbuffer) {
453     buffer = malloc(xmlbuflen);
454     if (!buffer)
455       goto out;
456     memcpy(buffer, xmlbuffer, xmlbuflen);
457     buflen = xmlbuflen;
458 
459   } else {
460     ret = hwloc_nolibxml_read_file(xmlpath, &buffer, &buflen);
461     if (ret < 0)
462       goto out;
463   }
464 
465   /* skip headers */
466   tmp = buffer;
467   while (!strncmp(tmp, "<?xml ", 6) || !strncmp(tmp, "<!DOCTYPE ", 10)) {
468     tmp = strchr(tmp, '\n');
469     if (!tmp)
470       goto out_with_buffer;
471     tmp++;
472   }
473 
474   state->global->next_attr = hwloc__nolibxml_import_next_attr;
475   state->global->find_child = hwloc__nolibxml_import_find_child;
476   state->global->close_tag = hwloc__nolibxml_import_close_tag;
477   state->global->close_child = hwloc__nolibxml_import_close_child;
478   state->global->get_content = hwloc__nolibxml_import_get_content;
479   state->global->close_content = hwloc__nolibxml_import_close_content;
480   state->parent = NULL;
481   nstate->closed = 0;
482   nstate->tagbuffer = tmp;
483   nstate->tagname = NULL;
484   nstate->attrbuffer = NULL;
485 
486   /* find root */
487   ret = hwloc__nolibxml_import_find_child(state, &childstate, &tag);
488   if (ret < 0)
489     goto out_with_buffer;
490   if (!tag || strcmp(tag, "topologydiff"))
491     goto out_with_buffer;
492 
493   while (1) {
494     char *attrname, *attrvalue;
495     if (hwloc__nolibxml_import_next_attr(&childstate, &attrname, &attrvalue) < 0)
496       break;
497     if (!strcmp(attrname, "refname")) {
498       free(refname);
499       refname = strdup(attrvalue);
500     } else
501       goto out_with_buffer;
502   }
503 
504   ret = hwloc__xml_import_diff(&childstate, firstdiffp);
505   if (refnamep && !ret)
506     *refnamep = refname;
507   else
508     free(refname);
509 
510   free(buffer);
511   return ret;
512 
513 out_with_buffer:
514   free(buffer);
515   free(refname);
516 out:
517   return -1;
518 }
519 
520 /*******************
521  * Export routines *
522  *******************/
523 
524 typedef struct hwloc__nolibxml_export_state_data_s {
525   char *buffer; /* (moving) buffer where to write */
526   size_t written; /* how many bytes were written (or would have be written if not truncated) */
527   size_t remaining; /* how many bytes are still available in the buffer */
528   unsigned indent; /* indentation level for the next line */
529   unsigned nr_children;
530   unsigned has_content;
531 } __hwloc_attribute_may_alias * hwloc__nolibxml_export_state_data_t;
532 
533 static void
hwloc__nolibxml_export_update_buffer(hwloc__nolibxml_export_state_data_t ndata,int res)534 hwloc__nolibxml_export_update_buffer(hwloc__nolibxml_export_state_data_t ndata, int res)
535 {
536   if (res >= 0) {
537     ndata->written += res;
538     if (res >= (int) ndata->remaining)
539       res = ndata->remaining>0 ? (int)ndata->remaining-1 : 0;
540     ndata->buffer += res;
541     ndata->remaining -= res;
542   }
543 }
544 
545 static char *
hwloc__nolibxml_export_escape_string(const char * src)546 hwloc__nolibxml_export_escape_string(const char *src)
547 {
548   size_t fulllen, sublen;
549   char *escaped, *dst;
550 
551   fulllen = strlen(src);
552 
553   sublen = strcspn(src, "\n\r\t\"<>&");
554   if (sublen == fulllen)
555     return NULL; /* nothing to escape */
556 
557   escaped = malloc(fulllen*6+1); /* escaped chars are replaced by at most 6 char */
558   dst = escaped;
559 
560   memcpy(dst, src, sublen);
561   src += sublen;
562   dst += sublen;
563 
564   while (*src) {
565     int replen;
566     switch (*src) {
567     case '\n': strcpy(dst, "&#10;");  replen=5; break;
568     case '\r': strcpy(dst, "&#13;");  replen=5; break;
569     case '\t': strcpy(dst, "&#9;");   replen=4; break;
570     case '\"': strcpy(dst, "&quot;"); replen=6; break;
571     case '<':  strcpy(dst, "&lt;");   replen=4; break;
572     case '>':  strcpy(dst, "&gt;");   replen=4; break;
573     case '&':  strcpy(dst, "&amp;");  replen=5; break;
574     default: replen=0; break;
575     }
576     dst+=replen; src++;
577 
578     sublen = strcspn(src, "\n\r\t\"<>&");
579     memcpy(dst, src, sublen);
580     src += sublen;
581     dst += sublen;
582   }
583 
584   *dst = 0;
585   return escaped;
586 }
587 
588 static void
hwloc__nolibxml_export_new_child(hwloc__xml_export_state_t parentstate,hwloc__xml_export_state_t state,const char * name)589 hwloc__nolibxml_export_new_child(hwloc__xml_export_state_t parentstate,
590 				 hwloc__xml_export_state_t state,
591 				 const char *name)
592 {
593   hwloc__nolibxml_export_state_data_t npdata = (void *) parentstate->data;
594   hwloc__nolibxml_export_state_data_t ndata = (void *) state->data;
595   int res;
596 
597   assert(!npdata->has_content);
598   if (!npdata->nr_children) {
599     res = hwloc_snprintf(npdata->buffer, npdata->remaining, ">\n");
600     hwloc__nolibxml_export_update_buffer(npdata, res);
601   }
602   npdata->nr_children++;
603 
604   state->parent = parentstate;
605   state->new_child = parentstate->new_child;
606   state->new_prop = parentstate->new_prop;
607   state->add_content = parentstate->add_content;
608   state->end_object = parentstate->end_object;
609   state->global = parentstate->global;
610 
611   ndata->buffer = npdata->buffer;
612   ndata->written = npdata->written;
613   ndata->remaining = npdata->remaining;
614   ndata->indent = npdata->indent + 2;
615 
616   ndata->nr_children = 0;
617   ndata->has_content = 0;
618 
619   res = hwloc_snprintf(ndata->buffer, ndata->remaining, "%*s<%s", (int) npdata->indent, "", name);
620   hwloc__nolibxml_export_update_buffer(ndata, res);
621 }
622 
623 static void
hwloc__nolibxml_export_new_prop(hwloc__xml_export_state_t state,const char * name,const char * value)624 hwloc__nolibxml_export_new_prop(hwloc__xml_export_state_t state, const char *name, const char *value)
625 {
626   hwloc__nolibxml_export_state_data_t ndata = (void *) state->data;
627   char *escaped = hwloc__nolibxml_export_escape_string(value);
628   int res = hwloc_snprintf(ndata->buffer, ndata->remaining, " %s=\"%s\"", name, escaped ? (const char *) escaped : value);
629   hwloc__nolibxml_export_update_buffer(ndata, res);
630   free(escaped);
631 }
632 
633 static void
hwloc__nolibxml_export_end_object(hwloc__xml_export_state_t state,const char * name)634 hwloc__nolibxml_export_end_object(hwloc__xml_export_state_t state, const char *name)
635 {
636   hwloc__nolibxml_export_state_data_t ndata = (void *) state->data;
637   hwloc__nolibxml_export_state_data_t npdata = (void *) state->parent->data;
638   int res;
639 
640   assert (!(ndata->has_content && ndata->nr_children));
641   if (ndata->has_content) {
642     res = hwloc_snprintf(ndata->buffer, ndata->remaining, "</%s>\n", name);
643   } else if (ndata->nr_children) {
644     res = hwloc_snprintf(ndata->buffer, ndata->remaining, "%*s</%s>\n", (int) npdata->indent, "", name);
645   } else {
646     res = hwloc_snprintf(ndata->buffer, ndata->remaining, "/>\n");
647   }
648   hwloc__nolibxml_export_update_buffer(ndata, res);
649 
650   npdata->buffer = ndata->buffer;
651   npdata->written = ndata->written;
652   npdata->remaining = ndata->remaining;
653 }
654 
655 static void
hwloc__nolibxml_export_add_content(hwloc__xml_export_state_t state,const char * buffer,size_t length __hwloc_attribute_unused)656 hwloc__nolibxml_export_add_content(hwloc__xml_export_state_t state, const char *buffer, size_t length __hwloc_attribute_unused)
657 {
658   hwloc__nolibxml_export_state_data_t ndata = (void *) state->data;
659   int res;
660 
661   assert(!ndata->nr_children);
662   if (!ndata->has_content) {
663     res = hwloc_snprintf(ndata->buffer, ndata->remaining, ">");
664     hwloc__nolibxml_export_update_buffer(ndata, res);
665   }
666   ndata->has_content = 1;
667 
668   res = hwloc_snprintf(ndata->buffer, ndata->remaining, "%s", buffer);
669   hwloc__nolibxml_export_update_buffer(ndata, res);
670 }
671 
672 static size_t
hwloc___nolibxml_prepare_export(hwloc_topology_t topology,struct hwloc__xml_export_data_s * edata,char * xmlbuffer,int buflen,unsigned long flags)673 hwloc___nolibxml_prepare_export(hwloc_topology_t topology, struct hwloc__xml_export_data_s *edata,
674 				char *xmlbuffer, int buflen, unsigned long flags)
675 {
676   struct hwloc__xml_export_state_s state, childstate;
677   hwloc__nolibxml_export_state_data_t ndata = (void *) &state.data;
678   int v1export = flags & HWLOC_TOPOLOGY_EXPORT_XML_FLAG_V1;
679   int res;
680 
681   HWLOC_BUILD_ASSERT(sizeof(*ndata) <= sizeof(state.data));
682 
683   state.new_child = hwloc__nolibxml_export_new_child;
684   state.new_prop = hwloc__nolibxml_export_new_prop;
685   state.add_content = hwloc__nolibxml_export_add_content;
686   state.end_object = hwloc__nolibxml_export_end_object;
687   state.global = edata;
688 
689   ndata->indent = 0;
690   ndata->written = 0;
691   ndata->buffer = xmlbuffer;
692   ndata->remaining = buflen;
693 
694   ndata->nr_children = 1; /* don't close a non-existing previous tag when opening the topology tag */
695   ndata->has_content = 0;
696 
697   res = hwloc_snprintf(ndata->buffer, ndata->remaining,
698 		 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
699 		 "<!DOCTYPE topology SYSTEM \"%s\">\n", v1export ? "hwloc.dtd" : "hwloc2.dtd");
700   hwloc__nolibxml_export_update_buffer(ndata, res);
701   hwloc__nolibxml_export_new_child(&state, &childstate, "topology");
702   if (!(flags & HWLOC_TOPOLOGY_EXPORT_XML_FLAG_V1))
703     hwloc__nolibxml_export_new_prop(&childstate, "version", "2.0");
704   hwloc__xml_export_topology (&childstate, topology, flags);
705   hwloc__nolibxml_export_end_object(&childstate, "topology");
706 
707   return ndata->written+1; /* ending \0 */
708 }
709 
710 static int
hwloc_nolibxml_export_buffer(hwloc_topology_t topology,struct hwloc__xml_export_data_s * edata,char ** bufferp,int * buflenp,unsigned long flags)711 hwloc_nolibxml_export_buffer(hwloc_topology_t topology, struct hwloc__xml_export_data_s *edata,
712 			     char **bufferp, int *buflenp, unsigned long flags)
713 {
714   char *buffer;
715   size_t bufferlen, res;
716 
717   bufferlen = 16384; /* random guess for large enough default */
718   buffer = malloc(bufferlen);
719   if (!buffer)
720     return -1;
721   res = hwloc___nolibxml_prepare_export(topology, edata, buffer, (int)bufferlen, flags);
722 
723   if (res > bufferlen) {
724     char *tmp = realloc(buffer, res);
725     if (!tmp) {
726       free(buffer);
727       return -1;
728     }
729     buffer = tmp;
730     hwloc___nolibxml_prepare_export(topology, edata, buffer, (int)res, flags);
731   }
732 
733   *bufferp = buffer;
734   *buflenp = (int)res;
735   return 0;
736 }
737 
738 static int
hwloc_nolibxml_export_file(hwloc_topology_t topology,struct hwloc__xml_export_data_s * edata,const char * filename,unsigned long flags)739 hwloc_nolibxml_export_file(hwloc_topology_t topology, struct hwloc__xml_export_data_s *edata,
740 			   const char *filename, unsigned long flags)
741 {
742   FILE *file;
743   char *buffer;
744   int bufferlen;
745   int ret;
746 
747   ret = hwloc_nolibxml_export_buffer(topology, edata, &buffer, &bufferlen, flags);
748   if (ret < 0)
749     return -1;
750 
751   if (!strcmp(filename, "-")) {
752     file = stdout;
753   } else {
754     file = fopen(filename, "w");
755     if (!file) {
756       free(buffer);
757       return -1;
758     }
759   }
760 
761   ret = (int)fwrite(buffer, 1, bufferlen-1 /* don't write the ending \0 */, file);
762   if (ret == bufferlen-1) {
763     ret = 0;
764   } else {
765     errno = ferror(file);
766     ret = -1;
767   }
768 
769   free(buffer);
770 
771   if (file != stdout)
772     fclose(file);
773   return ret;
774 }
775 
776 static size_t
hwloc___nolibxml_prepare_export_diff(hwloc_topology_diff_t diff,const char * refname,char * xmlbuffer,int buflen)777 hwloc___nolibxml_prepare_export_diff(hwloc_topology_diff_t diff, const char *refname, char *xmlbuffer, int buflen)
778 {
779   struct hwloc__xml_export_state_s state, childstate;
780   hwloc__nolibxml_export_state_data_t ndata = (void *) &state.data;
781   int res;
782 
783   HWLOC_BUILD_ASSERT(sizeof(*ndata) <= sizeof(state.data));
784 
785   state.new_child = hwloc__nolibxml_export_new_child;
786   state.new_prop = hwloc__nolibxml_export_new_prop;
787   state.add_content = hwloc__nolibxml_export_add_content;
788   state.end_object = hwloc__nolibxml_export_end_object;
789   state.global = NULL;
790 
791   ndata->indent = 0;
792   ndata->written = 0;
793   ndata->buffer = xmlbuffer;
794   ndata->remaining = buflen;
795 
796   ndata->nr_children = 1; /* don't close a non-existing previous tag when opening the topology tag */
797   ndata->has_content = 0;
798 
799   res = hwloc_snprintf(ndata->buffer, ndata->remaining,
800 		 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
801 		 "<!DOCTYPE topologydiff SYSTEM \"hwloc2-diff.dtd\">\n");
802   hwloc__nolibxml_export_update_buffer(ndata, res);
803   hwloc__nolibxml_export_new_child(&state, &childstate, "topologydiff");
804   if (refname)
805     hwloc__nolibxml_export_new_prop(&childstate, "refname", refname);
806   hwloc__xml_export_diff (&childstate, diff);
807   hwloc__nolibxml_export_end_object(&childstate, "topologydiff");
808 
809   return ndata->written+1;
810 }
811 
812 static int
hwloc_nolibxml_export_diff_buffer(hwloc_topology_diff_t diff,const char * refname,char ** bufferp,int * buflenp)813 hwloc_nolibxml_export_diff_buffer(hwloc_topology_diff_t diff, const char *refname, char **bufferp, int *buflenp)
814 {
815   char *buffer;
816   size_t bufferlen, res;
817 
818   bufferlen = 16384; /* random guess for large enough default */
819   buffer = malloc(bufferlen);
820   if (!buffer)
821     return -1;
822   res = hwloc___nolibxml_prepare_export_diff(diff, refname, buffer, (int)bufferlen);
823 
824   if (res > bufferlen) {
825     char *tmp = realloc(buffer, res);
826     if (!tmp) {
827       free(buffer);
828       return -1;
829     }
830     buffer = tmp;
831     hwloc___nolibxml_prepare_export_diff(diff, refname, buffer, (int)res);
832   }
833 
834   *bufferp = buffer;
835   *buflenp = (int)res;
836   return 0;
837 }
838 
839 static int
hwloc_nolibxml_export_diff_file(hwloc_topology_diff_t diff,const char * refname,const char * filename)840 hwloc_nolibxml_export_diff_file(hwloc_topology_diff_t diff, const char *refname, const char *filename)
841 {
842   FILE *file;
843   char *buffer;
844   int bufferlen;
845   int ret;
846 
847   ret = hwloc_nolibxml_export_diff_buffer(diff, refname, &buffer, &bufferlen);
848   if (ret < 0)
849     return -1;
850 
851   if (!strcmp(filename, "-")) {
852     file = stdout;
853   } else {
854     file = fopen(filename, "w");
855     if (!file) {
856       free(buffer);
857       return -1;
858     }
859   }
860 
861   ret = (int)fwrite(buffer, 1, bufferlen-1 /* don't write the ending \0 */, file);
862   if (ret == bufferlen-1) {
863     ret = 0;
864   } else {
865     errno = ferror(file);
866     ret = -1;
867   }
868 
869   free(buffer);
870 
871   if (file != stdout)
872     fclose(file);
873   return ret;
874 }
875 
876 static void
hwloc_nolibxml_free_buffer(void * xmlbuffer)877 hwloc_nolibxml_free_buffer(void *xmlbuffer)
878 {
879   free(xmlbuffer);
880 }
881 
882 /*************
883  * Callbacks *
884  *************/
885 
886 static struct hwloc_xml_callbacks hwloc_xml_nolibxml_callbacks = {
887   hwloc_nolibxml_backend_init,
888   hwloc_nolibxml_export_file,
889   hwloc_nolibxml_export_buffer,
890   hwloc_nolibxml_free_buffer,
891   hwloc_nolibxml_import_diff,
892   hwloc_nolibxml_export_diff_file,
893   hwloc_nolibxml_export_diff_buffer
894 };
895 
896 static struct hwloc_xml_component hwloc_nolibxml_xml_component = {
897   &hwloc_xml_nolibxml_callbacks,
898   NULL
899 };
900 
901 const struct hwloc_component hwloc_xml_nolibxml_component = {
902   HWLOC_COMPONENT_ABI,
903   NULL, NULL,
904   HWLOC_COMPONENT_TYPE_XML,
905   0,
906   &hwloc_nolibxml_xml_component
907 };
908