xmlsave.c 78 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685
  1. /*
  2. * xmlsave.c: Implementation of the document serializer
  3. *
  4. * See Copyright for the status of this software.
  5. *
  6. * daniel@veillard.com
  7. */
  8. #define IN_LIBXML
  9. #include "libxml.h"
  10. #include <string.h>
  11. #include <libxml/xmlmemory.h>
  12. #include <libxml/parserInternals.h>
  13. #include <libxml/tree.h>
  14. #include <libxml/xmlsave.h>
  15. #define MAX_INDENT 60
  16. #include <libxml/HTMLtree.h>
  17. #include "private/buf.h"
  18. #include "private/enc.h"
  19. #include "private/error.h"
  20. #include "private/save.h"
  21. #ifdef LIBXML_OUTPUT_ENABLED
  22. #define XHTML_NS_NAME BAD_CAST "http://www.w3.org/1999/xhtml"
  23. #define TODO \
  24. xmlGenericError(xmlGenericErrorContext, \
  25. "Unimplemented block at %s:%d\n", \
  26. __FILE__, __LINE__);
  27. struct _xmlSaveCtxt {
  28. void *_private;
  29. int type;
  30. int fd;
  31. const xmlChar *filename;
  32. const xmlChar *encoding;
  33. xmlCharEncodingHandlerPtr handler;
  34. xmlOutputBufferPtr buf;
  35. int options;
  36. int level;
  37. int format;
  38. char indent[MAX_INDENT + 1]; /* array for indenting output */
  39. int indent_nr;
  40. int indent_size;
  41. xmlCharEncodingOutputFunc escape; /* used for element content */
  42. xmlCharEncodingOutputFunc escapeAttr;/* used for attribute content */
  43. };
  44. /************************************************************************
  45. * *
  46. * Output error handlers *
  47. * *
  48. ************************************************************************/
  49. /**
  50. * xmlSaveErrMemory:
  51. * @extra: extra information
  52. *
  53. * Handle an out of memory condition
  54. */
  55. static void
  56. xmlSaveErrMemory(const char *extra)
  57. {
  58. __xmlSimpleError(XML_FROM_OUTPUT, XML_ERR_NO_MEMORY, NULL, NULL, extra);
  59. }
  60. /**
  61. * xmlSaveErr:
  62. * @code: the error number
  63. * @node: the location of the error.
  64. * @extra: extra information
  65. *
  66. * Handle an out of memory condition
  67. */
  68. static void
  69. xmlSaveErr(int code, xmlNodePtr node, const char *extra)
  70. {
  71. const char *msg = NULL;
  72. switch(code) {
  73. case XML_SAVE_NOT_UTF8:
  74. msg = "string is not in UTF-8\n";
  75. break;
  76. case XML_SAVE_CHAR_INVALID:
  77. msg = "invalid character value\n";
  78. break;
  79. case XML_SAVE_UNKNOWN_ENCODING:
  80. msg = "unknown encoding %s\n";
  81. break;
  82. case XML_SAVE_NO_DOCTYPE:
  83. msg = "document has no DOCTYPE\n";
  84. break;
  85. default:
  86. msg = "unexpected error number\n";
  87. }
  88. __xmlSimpleError(XML_FROM_OUTPUT, code, node, msg, extra);
  89. }
  90. /************************************************************************
  91. * *
  92. * Special escaping routines *
  93. * *
  94. ************************************************************************/
  95. static unsigned char *
  96. xmlSerializeHexCharRef(unsigned char *out, int val) {
  97. unsigned char *ptr;
  98. *out++ = '&';
  99. *out++ = '#';
  100. *out++ = 'x';
  101. if (val < 0x10) ptr = out;
  102. else if (val < 0x100) ptr = out + 1;
  103. else if (val < 0x1000) ptr = out + 2;
  104. else if (val < 0x10000) ptr = out + 3;
  105. else if (val < 0x100000) ptr = out + 4;
  106. else ptr = out + 5;
  107. out = ptr + 1;
  108. while (val > 0) {
  109. switch (val & 0xF) {
  110. case 0: *ptr-- = '0'; break;
  111. case 1: *ptr-- = '1'; break;
  112. case 2: *ptr-- = '2'; break;
  113. case 3: *ptr-- = '3'; break;
  114. case 4: *ptr-- = '4'; break;
  115. case 5: *ptr-- = '5'; break;
  116. case 6: *ptr-- = '6'; break;
  117. case 7: *ptr-- = '7'; break;
  118. case 8: *ptr-- = '8'; break;
  119. case 9: *ptr-- = '9'; break;
  120. case 0xA: *ptr-- = 'A'; break;
  121. case 0xB: *ptr-- = 'B'; break;
  122. case 0xC: *ptr-- = 'C'; break;
  123. case 0xD: *ptr-- = 'D'; break;
  124. case 0xE: *ptr-- = 'E'; break;
  125. case 0xF: *ptr-- = 'F'; break;
  126. default: *ptr-- = '0'; break;
  127. }
  128. val >>= 4;
  129. }
  130. *out++ = ';';
  131. *out = 0;
  132. return(out);
  133. }
  134. /**
  135. * xmlEscapeEntities:
  136. * @out: a pointer to an array of bytes to store the result
  137. * @outlen: the length of @out
  138. * @in: a pointer to an array of unescaped UTF-8 bytes
  139. * @inlen: the length of @in
  140. *
  141. * Take a block of UTF-8 chars in and escape them. Used when there is no
  142. * encoding specified.
  143. *
  144. * Returns 0 if success, or -1 otherwise
  145. * The value of @inlen after return is the number of octets consumed
  146. * if the return value is positive, else unpredictable.
  147. * The value of @outlen after return is the number of octets consumed.
  148. */
  149. static int
  150. xmlEscapeEntities(unsigned char* out, int *outlen,
  151. const xmlChar* in, int *inlen) {
  152. unsigned char* outstart = out;
  153. const unsigned char* base = in;
  154. unsigned char* outend = out + *outlen;
  155. const unsigned char* inend;
  156. int val;
  157. inend = in + (*inlen);
  158. while ((in < inend) && (out < outend)) {
  159. if (*in == '<') {
  160. if (outend - out < 4) break;
  161. *out++ = '&';
  162. *out++ = 'l';
  163. *out++ = 't';
  164. *out++ = ';';
  165. in++;
  166. continue;
  167. } else if (*in == '>') {
  168. if (outend - out < 4) break;
  169. *out++ = '&';
  170. *out++ = 'g';
  171. *out++ = 't';
  172. *out++ = ';';
  173. in++;
  174. continue;
  175. } else if (*in == '&') {
  176. if (outend - out < 5) break;
  177. *out++ = '&';
  178. *out++ = 'a';
  179. *out++ = 'm';
  180. *out++ = 'p';
  181. *out++ = ';';
  182. in++;
  183. continue;
  184. } else if (((*in >= 0x20) && (*in < 0x80)) ||
  185. (*in == '\n') || (*in == '\t')) {
  186. /*
  187. * default case, just copy !
  188. */
  189. *out++ = *in++;
  190. continue;
  191. } else if (*in >= 0x80) {
  192. /*
  193. * We assume we have UTF-8 input.
  194. */
  195. if (outend - out < 11) break;
  196. if (*in < 0xC0) {
  197. xmlSaveErr(XML_SAVE_NOT_UTF8, NULL, NULL);
  198. in++;
  199. goto error;
  200. } else if (*in < 0xE0) {
  201. if (inend - in < 2) break;
  202. val = (in[0]) & 0x1F;
  203. val <<= 6;
  204. val |= (in[1]) & 0x3F;
  205. in += 2;
  206. } else if (*in < 0xF0) {
  207. if (inend - in < 3) break;
  208. val = (in[0]) & 0x0F;
  209. val <<= 6;
  210. val |= (in[1]) & 0x3F;
  211. val <<= 6;
  212. val |= (in[2]) & 0x3F;
  213. in += 3;
  214. } else if (*in < 0xF8) {
  215. if (inend - in < 4) break;
  216. val = (in[0]) & 0x07;
  217. val <<= 6;
  218. val |= (in[1]) & 0x3F;
  219. val <<= 6;
  220. val |= (in[2]) & 0x3F;
  221. val <<= 6;
  222. val |= (in[3]) & 0x3F;
  223. in += 4;
  224. } else {
  225. xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL);
  226. in++;
  227. goto error;
  228. }
  229. if (!IS_CHAR(val)) {
  230. xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL);
  231. in++;
  232. goto error;
  233. }
  234. /*
  235. * We could do multiple things here. Just save as a char ref
  236. */
  237. out = xmlSerializeHexCharRef(out, val);
  238. } else if (IS_BYTE_CHAR(*in)) {
  239. if (outend - out < 6) break;
  240. out = xmlSerializeHexCharRef(out, *in++);
  241. } else {
  242. xmlGenericError(xmlGenericErrorContext,
  243. "xmlEscapeEntities : char out of range\n");
  244. in++;
  245. goto error;
  246. }
  247. }
  248. *outlen = out - outstart;
  249. *inlen = in - base;
  250. return(0);
  251. error:
  252. *outlen = out - outstart;
  253. *inlen = in - base;
  254. return(-1);
  255. }
  256. /************************************************************************
  257. * *
  258. * Allocation and deallocation *
  259. * *
  260. ************************************************************************/
  261. /**
  262. * xmlSaveCtxtInit:
  263. * @ctxt: the saving context
  264. *
  265. * Initialize a saving context
  266. */
  267. static void
  268. xmlSaveCtxtInit(xmlSaveCtxtPtr ctxt)
  269. {
  270. int i;
  271. int len;
  272. if (ctxt == NULL) return;
  273. if ((ctxt->encoding == NULL) && (ctxt->escape == NULL))
  274. ctxt->escape = xmlEscapeEntities;
  275. len = xmlStrlen((xmlChar *)xmlTreeIndentString);
  276. if ((xmlTreeIndentString == NULL) || (len == 0)) {
  277. memset(&ctxt->indent[0], 0, MAX_INDENT + 1);
  278. } else {
  279. ctxt->indent_size = len;
  280. ctxt->indent_nr = MAX_INDENT / ctxt->indent_size;
  281. for (i = 0;i < ctxt->indent_nr;i++)
  282. memcpy(&ctxt->indent[i * ctxt->indent_size], xmlTreeIndentString,
  283. ctxt->indent_size);
  284. ctxt->indent[ctxt->indent_nr * ctxt->indent_size] = 0;
  285. }
  286. if (xmlSaveNoEmptyTags) {
  287. ctxt->options |= XML_SAVE_NO_EMPTY;
  288. }
  289. }
  290. /**
  291. * xmlFreeSaveCtxt:
  292. *
  293. * Free a saving context, destroying the output in any remaining buffer
  294. */
  295. static void
  296. xmlFreeSaveCtxt(xmlSaveCtxtPtr ctxt)
  297. {
  298. if (ctxt == NULL) return;
  299. if (ctxt->encoding != NULL)
  300. xmlFree((char *) ctxt->encoding);
  301. if (ctxt->buf != NULL)
  302. xmlOutputBufferClose(ctxt->buf);
  303. xmlFree(ctxt);
  304. }
  305. /**
  306. * xmlNewSaveCtxt:
  307. *
  308. * Create a new saving context
  309. *
  310. * Returns the new structure or NULL in case of error
  311. */
  312. static xmlSaveCtxtPtr
  313. xmlNewSaveCtxt(const char *encoding, int options)
  314. {
  315. xmlSaveCtxtPtr ret;
  316. ret = (xmlSaveCtxtPtr) xmlMalloc(sizeof(xmlSaveCtxt));
  317. if (ret == NULL) {
  318. xmlSaveErrMemory("creating saving context");
  319. return ( NULL );
  320. }
  321. memset(ret, 0, sizeof(xmlSaveCtxt));
  322. if (encoding != NULL) {
  323. ret->handler = xmlFindCharEncodingHandler(encoding);
  324. if (ret->handler == NULL) {
  325. xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL, encoding);
  326. xmlFreeSaveCtxt(ret);
  327. return(NULL);
  328. }
  329. ret->encoding = xmlStrdup((const xmlChar *)encoding);
  330. ret->escape = NULL;
  331. }
  332. xmlSaveCtxtInit(ret);
  333. /*
  334. * Use the options
  335. */
  336. /* Re-check this option as it may already have been set */
  337. if ((ret->options & XML_SAVE_NO_EMPTY) && ! (options & XML_SAVE_NO_EMPTY)) {
  338. options |= XML_SAVE_NO_EMPTY;
  339. }
  340. ret->options = options;
  341. if (options & XML_SAVE_FORMAT)
  342. ret->format = 1;
  343. else if (options & XML_SAVE_WSNONSIG)
  344. ret->format = 2;
  345. return(ret);
  346. }
  347. /************************************************************************
  348. * *
  349. * Dumping XML tree content to a simple buffer *
  350. * *
  351. ************************************************************************/
  352. /**
  353. * xmlAttrSerializeContent:
  354. * @buf: the XML buffer output
  355. * @doc: the document
  356. * @attr: the attribute pointer
  357. *
  358. * Serialize the attribute in the buffer
  359. */
  360. static void
  361. xmlAttrSerializeContent(xmlOutputBufferPtr buf, xmlAttrPtr attr)
  362. {
  363. xmlNodePtr children;
  364. children = attr->children;
  365. while (children != NULL) {
  366. switch (children->type) {
  367. case XML_TEXT_NODE:
  368. xmlBufAttrSerializeTxtContent(buf->buffer, attr->doc,
  369. attr, children->content);
  370. break;
  371. case XML_ENTITY_REF_NODE:
  372. xmlBufAdd(buf->buffer, BAD_CAST "&", 1);
  373. xmlBufAdd(buf->buffer, children->name,
  374. xmlStrlen(children->name));
  375. xmlBufAdd(buf->buffer, BAD_CAST ";", 1);
  376. break;
  377. default:
  378. /* should not happen unless we have a badly built tree */
  379. break;
  380. }
  381. children = children->next;
  382. }
  383. }
  384. /**
  385. * xmlBufDumpNotationTable:
  386. * @buf: an xmlBufPtr output
  387. * @table: A notation table
  388. *
  389. * This will dump the content of the notation table as an XML DTD definition
  390. */
  391. static void
  392. xmlBufDumpNotationTable(xmlBufPtr buf, xmlNotationTablePtr table) {
  393. xmlBufferPtr buffer;
  394. buffer = xmlBufferCreate();
  395. if (buffer == NULL) {
  396. /*
  397. * TODO set the error in buf
  398. */
  399. return;
  400. }
  401. xmlBufferSetAllocationScheme(buffer, XML_BUFFER_ALLOC_DOUBLEIT);
  402. xmlDumpNotationTable(buffer, table);
  403. xmlBufMergeBuffer(buf, buffer);
  404. }
  405. /**
  406. * xmlBufDumpElementDecl:
  407. * @buf: an xmlBufPtr output
  408. * @elem: An element table
  409. *
  410. * This will dump the content of the element declaration as an XML
  411. * DTD definition
  412. */
  413. static void
  414. xmlBufDumpElementDecl(xmlBufPtr buf, xmlElementPtr elem) {
  415. xmlBufferPtr buffer;
  416. buffer = xmlBufferCreate();
  417. if (buffer == NULL) {
  418. /*
  419. * TODO set the error in buf
  420. */
  421. return;
  422. }
  423. xmlBufferSetAllocationScheme(buffer, XML_BUFFER_ALLOC_DOUBLEIT);
  424. xmlDumpElementDecl(buffer, elem);
  425. xmlBufMergeBuffer(buf, buffer);
  426. }
  427. /**
  428. * xmlBufDumpAttributeDecl:
  429. * @buf: an xmlBufPtr output
  430. * @attr: An attribute declaration
  431. *
  432. * This will dump the content of the attribute declaration as an XML
  433. * DTD definition
  434. */
  435. static void
  436. xmlBufDumpAttributeDecl(xmlBufPtr buf, xmlAttributePtr attr) {
  437. xmlBufferPtr buffer;
  438. buffer = xmlBufferCreate();
  439. if (buffer == NULL) {
  440. /*
  441. * TODO set the error in buf
  442. */
  443. return;
  444. }
  445. xmlBufferSetAllocationScheme(buffer, XML_BUFFER_ALLOC_DOUBLEIT);
  446. xmlDumpAttributeDecl(buffer, attr);
  447. xmlBufMergeBuffer(buf, buffer);
  448. }
  449. /**
  450. * xmlBufDumpEntityDecl:
  451. * @buf: an xmlBufPtr output
  452. * @ent: An entity table
  453. *
  454. * This will dump the content of the entity table as an XML DTD definition
  455. */
  456. static void
  457. xmlBufDumpEntityDecl(xmlBufPtr buf, xmlEntityPtr ent) {
  458. xmlBufferPtr buffer;
  459. buffer = xmlBufferCreate();
  460. if (buffer == NULL) {
  461. /*
  462. * TODO set the error in buf
  463. */
  464. return;
  465. }
  466. xmlBufferSetAllocationScheme(buffer, XML_BUFFER_ALLOC_DOUBLEIT);
  467. xmlDumpEntityDecl(buffer, ent);
  468. xmlBufMergeBuffer(buf, buffer);
  469. }
  470. /************************************************************************
  471. * *
  472. * Dumping XML tree content to an I/O output buffer *
  473. * *
  474. ************************************************************************/
  475. static int xmlSaveSwitchEncoding(xmlSaveCtxtPtr ctxt, const char *encoding) {
  476. xmlOutputBufferPtr buf = ctxt->buf;
  477. if ((encoding != NULL) && (buf->encoder == NULL) && (buf->conv == NULL)) {
  478. buf->encoder = xmlFindCharEncodingHandler((const char *)encoding);
  479. if (buf->encoder == NULL) {
  480. xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL,
  481. (const char *)encoding);
  482. return(-1);
  483. }
  484. buf->conv = xmlBufCreate();
  485. if (buf->conv == NULL) {
  486. xmlCharEncCloseFunc(buf->encoder);
  487. xmlSaveErrMemory("creating encoding buffer");
  488. return(-1);
  489. }
  490. /*
  491. * initialize the state, e.g. if outputting a BOM
  492. */
  493. xmlCharEncOutput(buf, 1);
  494. }
  495. return(0);
  496. }
  497. static int xmlSaveClearEncoding(xmlSaveCtxtPtr ctxt) {
  498. xmlOutputBufferPtr buf = ctxt->buf;
  499. xmlOutputBufferFlush(buf);
  500. xmlCharEncCloseFunc(buf->encoder);
  501. xmlBufFree(buf->conv);
  502. buf->encoder = NULL;
  503. buf->conv = NULL;
  504. return(0);
  505. }
  506. #ifdef LIBXML_HTML_ENABLED
  507. static void
  508. xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
  509. #endif
  510. static void xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
  511. static int xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur);
  512. /**
  513. * xmlOutputBufferWriteWSNonSig:
  514. * @ctxt: The save context
  515. * @extra: Number of extra indents to apply to ctxt->level
  516. *
  517. * Write out formatting for non-significant whitespace output.
  518. */
  519. static void
  520. xmlOutputBufferWriteWSNonSig(xmlSaveCtxtPtr ctxt, int extra)
  521. {
  522. int i;
  523. if ((ctxt == NULL) || (ctxt->buf == NULL))
  524. return;
  525. xmlOutputBufferWrite(ctxt->buf, 1, "\n");
  526. for (i = 0; i < (ctxt->level + extra); i += ctxt->indent_nr) {
  527. xmlOutputBufferWrite(ctxt->buf, ctxt->indent_size *
  528. ((ctxt->level + extra - i) > ctxt->indent_nr ?
  529. ctxt->indent_nr : (ctxt->level + extra - i)),
  530. ctxt->indent);
  531. }
  532. }
  533. /**
  534. * xmlNsDumpOutput:
  535. * @buf: the XML buffer output
  536. * @cur: a namespace
  537. * @ctxt: the output save context. Optional.
  538. *
  539. * Dump a local Namespace definition.
  540. * Should be called in the context of attributes dumps.
  541. * If @ctxt is supplied, @buf should be its buffer.
  542. */
  543. static void
  544. xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur, xmlSaveCtxtPtr ctxt) {
  545. if ((cur == NULL) || (buf == NULL)) return;
  546. if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) {
  547. if (xmlStrEqual(cur->prefix, BAD_CAST "xml"))
  548. return;
  549. if (ctxt != NULL && ctxt->format == 2)
  550. xmlOutputBufferWriteWSNonSig(ctxt, 2);
  551. else
  552. xmlOutputBufferWrite(buf, 1, " ");
  553. /* Within the context of an element attributes */
  554. if (cur->prefix != NULL) {
  555. xmlOutputBufferWrite(buf, 6, "xmlns:");
  556. xmlOutputBufferWriteString(buf, (const char *)cur->prefix);
  557. } else
  558. xmlOutputBufferWrite(buf, 5, "xmlns");
  559. xmlOutputBufferWrite(buf, 1, "=");
  560. xmlBufWriteQuotedString(buf->buffer, cur->href);
  561. }
  562. }
  563. /**
  564. * xmlNsDumpOutputCtxt
  565. * @ctxt: the save context
  566. * @cur: a namespace
  567. *
  568. * Dump a local Namespace definition to a save context.
  569. * Should be called in the context of attribute dumps.
  570. */
  571. static void
  572. xmlNsDumpOutputCtxt(xmlSaveCtxtPtr ctxt, xmlNsPtr cur) {
  573. xmlNsDumpOutput(ctxt->buf, cur, ctxt);
  574. }
  575. /**
  576. * xmlNsListDumpOutputCtxt
  577. * @ctxt: the save context
  578. * @cur: the first namespace
  579. *
  580. * Dump a list of local namespace definitions to a save context.
  581. * Should be called in the context of attribute dumps.
  582. */
  583. static void
  584. xmlNsListDumpOutputCtxt(xmlSaveCtxtPtr ctxt, xmlNsPtr cur) {
  585. while (cur != NULL) {
  586. xmlNsDumpOutput(ctxt->buf, cur, ctxt);
  587. cur = cur->next;
  588. }
  589. }
  590. /**
  591. * xmlNsListDumpOutput:
  592. * @buf: the XML buffer output
  593. * @cur: the first namespace
  594. *
  595. * Dump a list of local Namespace definitions.
  596. * Should be called in the context of attributes dumps.
  597. */
  598. void
  599. xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
  600. while (cur != NULL) {
  601. xmlNsDumpOutput(buf, cur, NULL);
  602. cur = cur->next;
  603. }
  604. }
  605. /**
  606. * xmlDtdDumpOutput:
  607. * @buf: the XML buffer output
  608. * @dtd: the pointer to the DTD
  609. *
  610. * Dump the XML document DTD, if any.
  611. */
  612. static void
  613. xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt, xmlDtdPtr dtd) {
  614. xmlOutputBufferPtr buf;
  615. xmlNodePtr cur;
  616. int format, level;
  617. if (dtd == NULL) return;
  618. if ((ctxt == NULL) || (ctxt->buf == NULL))
  619. return;
  620. buf = ctxt->buf;
  621. xmlOutputBufferWrite(buf, 10, "<!DOCTYPE ");
  622. xmlOutputBufferWriteString(buf, (const char *)dtd->name);
  623. if (dtd->ExternalID != NULL) {
  624. xmlOutputBufferWrite(buf, 8, " PUBLIC ");
  625. xmlBufWriteQuotedString(buf->buffer, dtd->ExternalID);
  626. xmlOutputBufferWrite(buf, 1, " ");
  627. xmlBufWriteQuotedString(buf->buffer, dtd->SystemID);
  628. } else if (dtd->SystemID != NULL) {
  629. xmlOutputBufferWrite(buf, 8, " SYSTEM ");
  630. xmlBufWriteQuotedString(buf->buffer, dtd->SystemID);
  631. }
  632. if ((dtd->entities == NULL) && (dtd->elements == NULL) &&
  633. (dtd->attributes == NULL) && (dtd->notations == NULL) &&
  634. (dtd->pentities == NULL)) {
  635. xmlOutputBufferWrite(buf, 1, ">");
  636. return;
  637. }
  638. xmlOutputBufferWrite(buf, 3, " [\n");
  639. /*
  640. * Dump the notations first they are not in the DTD children list
  641. * Do this only on a standalone DTD or on the internal subset though.
  642. */
  643. if ((dtd->notations != NULL) && ((dtd->doc == NULL) ||
  644. (dtd->doc->intSubset == dtd))) {
  645. xmlBufDumpNotationTable(buf->buffer,
  646. (xmlNotationTablePtr) dtd->notations);
  647. }
  648. format = ctxt->format;
  649. level = ctxt->level;
  650. ctxt->format = 0;
  651. ctxt->level = -1;
  652. for (cur = dtd->children; cur != NULL; cur = cur->next) {
  653. xmlNodeDumpOutputInternal(ctxt, cur);
  654. }
  655. ctxt->format = format;
  656. ctxt->level = level;
  657. xmlOutputBufferWrite(buf, 2, "]>");
  658. }
  659. /**
  660. * xmlAttrDumpOutput:
  661. * @buf: the XML buffer output
  662. * @cur: the attribute pointer
  663. *
  664. * Dump an XML attribute
  665. */
  666. static void
  667. xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
  668. xmlOutputBufferPtr buf;
  669. if (cur == NULL) return;
  670. buf = ctxt->buf;
  671. if (buf == NULL) return;
  672. if (ctxt->format == 2)
  673. xmlOutputBufferWriteWSNonSig(ctxt, 2);
  674. else
  675. xmlOutputBufferWrite(buf, 1, " ");
  676. if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
  677. xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
  678. xmlOutputBufferWrite(buf, 1, ":");
  679. }
  680. xmlOutputBufferWriteString(buf, (const char *)cur->name);
  681. xmlOutputBufferWrite(buf, 2, "=\"");
  682. xmlAttrSerializeContent(buf, cur);
  683. xmlOutputBufferWrite(buf, 1, "\"");
  684. }
  685. #ifdef LIBXML_HTML_ENABLED
  686. /**
  687. * htmlNodeDumpOutputInternal:
  688. * @cur: the current node
  689. *
  690. * Dump an HTML node, recursive behaviour, children are printed too.
  691. */
  692. static int
  693. htmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
  694. const xmlChar *oldenc = NULL;
  695. const xmlChar *oldctxtenc = ctxt->encoding;
  696. const xmlChar *encoding = ctxt->encoding;
  697. xmlOutputBufferPtr buf = ctxt->buf;
  698. int switched_encoding = 0;
  699. xmlDocPtr doc;
  700. xmlInitParser();
  701. doc = cur->doc;
  702. if (doc != NULL) {
  703. oldenc = doc->encoding;
  704. if (ctxt->encoding != NULL) {
  705. doc->encoding = BAD_CAST ctxt->encoding;
  706. } else if (doc->encoding != NULL) {
  707. encoding = doc->encoding;
  708. }
  709. }
  710. if ((encoding != NULL) && (doc != NULL))
  711. htmlSetMetaEncoding(doc, (const xmlChar *) encoding);
  712. if ((encoding == NULL) && (doc != NULL))
  713. encoding = htmlGetMetaEncoding(doc);
  714. if (encoding == NULL)
  715. encoding = BAD_CAST "HTML";
  716. if ((encoding != NULL) && (oldctxtenc == NULL) &&
  717. (buf->encoder == NULL) && (buf->conv == NULL)) {
  718. if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
  719. doc->encoding = oldenc;
  720. return(-1);
  721. }
  722. switched_encoding = 1;
  723. }
  724. if (ctxt->options & XML_SAVE_FORMAT)
  725. htmlNodeDumpFormatOutput(buf, doc, cur,
  726. (const char *)encoding, 1);
  727. else
  728. htmlNodeDumpFormatOutput(buf, doc, cur,
  729. (const char *)encoding, 0);
  730. /*
  731. * Restore the state of the saving context at the end of the document
  732. */
  733. if ((switched_encoding) && (oldctxtenc == NULL)) {
  734. xmlSaveClearEncoding(ctxt);
  735. }
  736. if (doc != NULL)
  737. doc->encoding = oldenc;
  738. return(0);
  739. }
  740. #endif
  741. /**
  742. * xmlNodeDumpOutputInternal:
  743. * @cur: the current node
  744. *
  745. * Dump an XML node, recursive behaviour, children are printed too.
  746. */
  747. static void
  748. xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
  749. int format = ctxt->format;
  750. xmlNodePtr tmp, root, unformattedNode = NULL, parent;
  751. xmlAttrPtr attr;
  752. xmlChar *start, *end;
  753. xmlOutputBufferPtr buf;
  754. if (cur == NULL) return;
  755. buf = ctxt->buf;
  756. root = cur;
  757. parent = cur->parent;
  758. while (1) {
  759. switch (cur->type) {
  760. case XML_DOCUMENT_NODE:
  761. case XML_HTML_DOCUMENT_NODE:
  762. xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
  763. break;
  764. case XML_DTD_NODE:
  765. xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
  766. break;
  767. case XML_DOCUMENT_FRAG_NODE:
  768. /* Always validate cur->parent when descending. */
  769. if ((cur->parent == parent) && (cur->children != NULL)) {
  770. parent = cur;
  771. cur = cur->children;
  772. continue;
  773. }
  774. break;
  775. case XML_ELEMENT_DECL:
  776. xmlBufDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
  777. break;
  778. case XML_ATTRIBUTE_DECL:
  779. xmlBufDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
  780. break;
  781. case XML_ENTITY_DECL:
  782. xmlBufDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
  783. break;
  784. case XML_ELEMENT_NODE:
  785. if ((cur != root) && (ctxt->format == 1) &&
  786. (xmlIndentTreeOutput))
  787. xmlOutputBufferWrite(buf, ctxt->indent_size *
  788. (ctxt->level > ctxt->indent_nr ?
  789. ctxt->indent_nr : ctxt->level),
  790. ctxt->indent);
  791. /*
  792. * Some users like lxml are known to pass nodes with a corrupted
  793. * tree structure. Fall back to a recursive call to handle this
  794. * case.
  795. */
  796. if ((cur->parent != parent) && (cur->children != NULL)) {
  797. xmlNodeDumpOutputInternal(ctxt, cur);
  798. break;
  799. }
  800. xmlOutputBufferWrite(buf, 1, "<");
  801. if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
  802. xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
  803. xmlOutputBufferWrite(buf, 1, ":");
  804. }
  805. xmlOutputBufferWriteString(buf, (const char *)cur->name);
  806. if (cur->nsDef)
  807. xmlNsListDumpOutputCtxt(ctxt, cur->nsDef);
  808. for (attr = cur->properties; attr != NULL; attr = attr->next)
  809. xmlAttrDumpOutput(ctxt, attr);
  810. if (cur->children == NULL) {
  811. if ((ctxt->options & XML_SAVE_NO_EMPTY) == 0) {
  812. if (ctxt->format == 2)
  813. xmlOutputBufferWriteWSNonSig(ctxt, 0);
  814. xmlOutputBufferWrite(buf, 2, "/>");
  815. } else {
  816. if (ctxt->format == 2)
  817. xmlOutputBufferWriteWSNonSig(ctxt, 1);
  818. xmlOutputBufferWrite(buf, 3, "></");
  819. if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
  820. xmlOutputBufferWriteString(buf,
  821. (const char *)cur->ns->prefix);
  822. xmlOutputBufferWrite(buf, 1, ":");
  823. }
  824. xmlOutputBufferWriteString(buf, (const char *)cur->name);
  825. if (ctxt->format == 2)
  826. xmlOutputBufferWriteWSNonSig(ctxt, 0);
  827. xmlOutputBufferWrite(buf, 1, ">");
  828. }
  829. } else {
  830. if (ctxt->format == 1) {
  831. tmp = cur->children;
  832. while (tmp != NULL) {
  833. if ((tmp->type == XML_TEXT_NODE) ||
  834. (tmp->type == XML_CDATA_SECTION_NODE) ||
  835. (tmp->type == XML_ENTITY_REF_NODE)) {
  836. ctxt->format = 0;
  837. unformattedNode = cur;
  838. break;
  839. }
  840. tmp = tmp->next;
  841. }
  842. }
  843. if (ctxt->format == 2)
  844. xmlOutputBufferWriteWSNonSig(ctxt, 1);
  845. xmlOutputBufferWrite(buf, 1, ">");
  846. if (ctxt->format == 1) xmlOutputBufferWrite(buf, 1, "\n");
  847. if (ctxt->level >= 0) ctxt->level++;
  848. parent = cur;
  849. cur = cur->children;
  850. continue;
  851. }
  852. break;
  853. case XML_TEXT_NODE:
  854. if (cur->content == NULL)
  855. break;
  856. if (cur->name != xmlStringTextNoenc) {
  857. xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
  858. } else {
  859. /*
  860. * Disable escaping, needed for XSLT
  861. */
  862. xmlOutputBufferWriteString(buf, (const char *) cur->content);
  863. }
  864. break;
  865. case XML_PI_NODE:
  866. if ((cur != root) && (ctxt->format == 1) && (xmlIndentTreeOutput))
  867. xmlOutputBufferWrite(buf, ctxt->indent_size *
  868. (ctxt->level > ctxt->indent_nr ?
  869. ctxt->indent_nr : ctxt->level),
  870. ctxt->indent);
  871. if (cur->content != NULL) {
  872. xmlOutputBufferWrite(buf, 2, "<?");
  873. xmlOutputBufferWriteString(buf, (const char *)cur->name);
  874. if (cur->content != NULL) {
  875. if (ctxt->format == 2)
  876. xmlOutputBufferWriteWSNonSig(ctxt, 0);
  877. else
  878. xmlOutputBufferWrite(buf, 1, " ");
  879. xmlOutputBufferWriteString(buf,
  880. (const char *)cur->content);
  881. }
  882. xmlOutputBufferWrite(buf, 2, "?>");
  883. } else {
  884. xmlOutputBufferWrite(buf, 2, "<?");
  885. xmlOutputBufferWriteString(buf, (const char *)cur->name);
  886. if (ctxt->format == 2)
  887. xmlOutputBufferWriteWSNonSig(ctxt, 0);
  888. xmlOutputBufferWrite(buf, 2, "?>");
  889. }
  890. break;
  891. case XML_COMMENT_NODE:
  892. if ((cur != root) && (ctxt->format == 1) && (xmlIndentTreeOutput))
  893. xmlOutputBufferWrite(buf, ctxt->indent_size *
  894. (ctxt->level > ctxt->indent_nr ?
  895. ctxt->indent_nr : ctxt->level),
  896. ctxt->indent);
  897. if (cur->content != NULL) {
  898. xmlOutputBufferWrite(buf, 4, "<!--");
  899. xmlOutputBufferWriteString(buf, (const char *)cur->content);
  900. xmlOutputBufferWrite(buf, 3, "-->");
  901. }
  902. break;
  903. case XML_ENTITY_REF_NODE:
  904. xmlOutputBufferWrite(buf, 1, "&");
  905. xmlOutputBufferWriteString(buf, (const char *)cur->name);
  906. xmlOutputBufferWrite(buf, 1, ";");
  907. break;
  908. case XML_CDATA_SECTION_NODE:
  909. if (cur->content == NULL || *cur->content == '\0') {
  910. xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
  911. } else {
  912. start = end = cur->content;
  913. while (*end != '\0') {
  914. if ((*end == ']') && (*(end + 1) == ']') &&
  915. (*(end + 2) == '>')) {
  916. end = end + 2;
  917. xmlOutputBufferWrite(buf, 9, "<![CDATA[");
  918. xmlOutputBufferWrite(buf, end - start,
  919. (const char *)start);
  920. xmlOutputBufferWrite(buf, 3, "]]>");
  921. start = end;
  922. }
  923. end++;
  924. }
  925. if (start != end) {
  926. xmlOutputBufferWrite(buf, 9, "<![CDATA[");
  927. xmlOutputBufferWriteString(buf, (const char *)start);
  928. xmlOutputBufferWrite(buf, 3, "]]>");
  929. }
  930. }
  931. break;
  932. case XML_ATTRIBUTE_NODE:
  933. xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
  934. break;
  935. case XML_NAMESPACE_DECL:
  936. xmlNsDumpOutputCtxt(ctxt, (xmlNsPtr) cur);
  937. break;
  938. default:
  939. break;
  940. }
  941. while (1) {
  942. if (cur == root)
  943. return;
  944. if ((ctxt->format == 1) &&
  945. (cur->type != XML_XINCLUDE_START) &&
  946. (cur->type != XML_XINCLUDE_END))
  947. xmlOutputBufferWrite(buf, 1, "\n");
  948. if (cur->next != NULL) {
  949. cur = cur->next;
  950. break;
  951. }
  952. cur = parent;
  953. /* cur->parent was validated when descending. */
  954. parent = cur->parent;
  955. if (cur->type == XML_ELEMENT_NODE) {
  956. if (ctxt->level > 0) ctxt->level--;
  957. if ((xmlIndentTreeOutput) && (ctxt->format == 1))
  958. xmlOutputBufferWrite(buf, ctxt->indent_size *
  959. (ctxt->level > ctxt->indent_nr ?
  960. ctxt->indent_nr : ctxt->level),
  961. ctxt->indent);
  962. xmlOutputBufferWrite(buf, 2, "</");
  963. if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
  964. xmlOutputBufferWriteString(buf,
  965. (const char *)cur->ns->prefix);
  966. xmlOutputBufferWrite(buf, 1, ":");
  967. }
  968. xmlOutputBufferWriteString(buf, (const char *)cur->name);
  969. if (ctxt->format == 2)
  970. xmlOutputBufferWriteWSNonSig(ctxt, 0);
  971. xmlOutputBufferWrite(buf, 1, ">");
  972. if (cur == unformattedNode) {
  973. ctxt->format = format;
  974. unformattedNode = NULL;
  975. }
  976. }
  977. }
  978. }
  979. }
  980. /**
  981. * xmlDocContentDumpOutput:
  982. * @cur: the document
  983. *
  984. * Dump an XML document.
  985. */
  986. static int
  987. xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur) {
  988. #ifdef LIBXML_HTML_ENABLED
  989. xmlDtdPtr dtd;
  990. int is_xhtml = 0;
  991. #endif
  992. const xmlChar *oldenc = cur->encoding;
  993. const xmlChar *oldctxtenc = ctxt->encoding;
  994. const xmlChar *encoding = ctxt->encoding;
  995. xmlCharEncodingOutputFunc oldescape = ctxt->escape;
  996. xmlCharEncodingOutputFunc oldescapeAttr = ctxt->escapeAttr;
  997. xmlOutputBufferPtr buf = ctxt->buf;
  998. xmlCharEncoding enc;
  999. int switched_encoding = 0;
  1000. xmlInitParser();
  1001. if ((cur->type != XML_HTML_DOCUMENT_NODE) &&
  1002. (cur->type != XML_DOCUMENT_NODE))
  1003. return(-1);
  1004. if (ctxt->encoding != NULL) {
  1005. cur->encoding = BAD_CAST ctxt->encoding;
  1006. } else if (cur->encoding != NULL) {
  1007. encoding = cur->encoding;
  1008. }
  1009. if (((cur->type == XML_HTML_DOCUMENT_NODE) &&
  1010. ((ctxt->options & XML_SAVE_AS_XML) == 0) &&
  1011. ((ctxt->options & XML_SAVE_XHTML) == 0)) ||
  1012. (ctxt->options & XML_SAVE_AS_HTML)) {
  1013. #ifdef LIBXML_HTML_ENABLED
  1014. if (encoding != NULL)
  1015. htmlSetMetaEncoding(cur, (const xmlChar *) encoding);
  1016. if (encoding == NULL)
  1017. encoding = htmlGetMetaEncoding(cur);
  1018. if (encoding == NULL)
  1019. encoding = BAD_CAST "HTML";
  1020. if ((encoding != NULL) && (oldctxtenc == NULL) &&
  1021. (buf->encoder == NULL) && (buf->conv == NULL)) {
  1022. if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
  1023. cur->encoding = oldenc;
  1024. return(-1);
  1025. }
  1026. }
  1027. if (ctxt->options & XML_SAVE_FORMAT)
  1028. htmlDocContentDumpFormatOutput(buf, cur,
  1029. (const char *)encoding, 1);
  1030. else
  1031. htmlDocContentDumpFormatOutput(buf, cur,
  1032. (const char *)encoding, 0);
  1033. if (ctxt->encoding != NULL)
  1034. cur->encoding = oldenc;
  1035. return(0);
  1036. #else
  1037. return(-1);
  1038. #endif
  1039. } else if ((cur->type == XML_DOCUMENT_NODE) ||
  1040. (ctxt->options & XML_SAVE_AS_XML) ||
  1041. (ctxt->options & XML_SAVE_XHTML)) {
  1042. enc = xmlParseCharEncoding((const char*) encoding);
  1043. if ((encoding != NULL) && (oldctxtenc == NULL) &&
  1044. (buf->encoder == NULL) && (buf->conv == NULL) &&
  1045. ((ctxt->options & XML_SAVE_NO_DECL) == 0)) {
  1046. if ((enc != XML_CHAR_ENCODING_UTF8) &&
  1047. (enc != XML_CHAR_ENCODING_NONE) &&
  1048. (enc != XML_CHAR_ENCODING_ASCII)) {
  1049. /*
  1050. * we need to switch to this encoding but just for this
  1051. * document since we output the XMLDecl the conversion
  1052. * must be done to not generate not well formed documents.
  1053. */
  1054. if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
  1055. cur->encoding = oldenc;
  1056. return(-1);
  1057. }
  1058. switched_encoding = 1;
  1059. }
  1060. if (ctxt->escape == xmlEscapeEntities)
  1061. ctxt->escape = NULL;
  1062. if (ctxt->escapeAttr == xmlEscapeEntities)
  1063. ctxt->escapeAttr = NULL;
  1064. }
  1065. /*
  1066. * Save the XML declaration
  1067. */
  1068. if ((ctxt->options & XML_SAVE_NO_DECL) == 0) {
  1069. xmlOutputBufferWrite(buf, 14, "<?xml version=");
  1070. if (cur->version != NULL)
  1071. xmlBufWriteQuotedString(buf->buffer, cur->version);
  1072. else
  1073. xmlOutputBufferWrite(buf, 5, "\"1.0\"");
  1074. if (encoding != NULL) {
  1075. xmlOutputBufferWrite(buf, 10, " encoding=");
  1076. xmlBufWriteQuotedString(buf->buffer, (xmlChar *) encoding);
  1077. }
  1078. switch (cur->standalone) {
  1079. case 0:
  1080. xmlOutputBufferWrite(buf, 16, " standalone=\"no\"");
  1081. break;
  1082. case 1:
  1083. xmlOutputBufferWrite(buf, 17, " standalone=\"yes\"");
  1084. break;
  1085. }
  1086. xmlOutputBufferWrite(buf, 3, "?>\n");
  1087. }
  1088. #ifdef LIBXML_HTML_ENABLED
  1089. if (ctxt->options & XML_SAVE_XHTML)
  1090. is_xhtml = 1;
  1091. if ((ctxt->options & XML_SAVE_NO_XHTML) == 0) {
  1092. dtd = xmlGetIntSubset(cur);
  1093. if (dtd != NULL) {
  1094. is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
  1095. if (is_xhtml < 0) is_xhtml = 0;
  1096. }
  1097. }
  1098. #endif
  1099. if (cur->children != NULL) {
  1100. xmlNodePtr child = cur->children;
  1101. while (child != NULL) {
  1102. ctxt->level = 0;
  1103. #ifdef LIBXML_HTML_ENABLED
  1104. if (is_xhtml)
  1105. xhtmlNodeDumpOutput(ctxt, child);
  1106. else
  1107. #endif
  1108. xmlNodeDumpOutputInternal(ctxt, child);
  1109. if ((child->type != XML_XINCLUDE_START) &&
  1110. (child->type != XML_XINCLUDE_END))
  1111. xmlOutputBufferWrite(buf, 1, "\n");
  1112. child = child->next;
  1113. }
  1114. }
  1115. }
  1116. /*
  1117. * Restore the state of the saving context at the end of the document
  1118. */
  1119. if ((switched_encoding) && (oldctxtenc == NULL)) {
  1120. xmlSaveClearEncoding(ctxt);
  1121. ctxt->escape = oldescape;
  1122. ctxt->escapeAttr = oldescapeAttr;
  1123. }
  1124. cur->encoding = oldenc;
  1125. return(0);
  1126. }
  1127. #ifdef LIBXML_HTML_ENABLED
  1128. /************************************************************************
  1129. * *
  1130. * Functions specific to XHTML serialization *
  1131. * *
  1132. ************************************************************************/
  1133. /**
  1134. * xhtmlIsEmpty:
  1135. * @node: the node
  1136. *
  1137. * Check if a node is an empty xhtml node
  1138. *
  1139. * Returns 1 if the node is an empty node, 0 if not and -1 in case of error
  1140. */
  1141. static int
  1142. xhtmlIsEmpty(xmlNodePtr node) {
  1143. if (node == NULL)
  1144. return(-1);
  1145. if (node->type != XML_ELEMENT_NODE)
  1146. return(0);
  1147. if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME)))
  1148. return(0);
  1149. if (node->children != NULL)
  1150. return(0);
  1151. switch (node->name[0]) {
  1152. case 'a':
  1153. if (xmlStrEqual(node->name, BAD_CAST "area"))
  1154. return(1);
  1155. return(0);
  1156. case 'b':
  1157. if (xmlStrEqual(node->name, BAD_CAST "br"))
  1158. return(1);
  1159. if (xmlStrEqual(node->name, BAD_CAST "base"))
  1160. return(1);
  1161. if (xmlStrEqual(node->name, BAD_CAST "basefont"))
  1162. return(1);
  1163. return(0);
  1164. case 'c':
  1165. if (xmlStrEqual(node->name, BAD_CAST "col"))
  1166. return(1);
  1167. return(0);
  1168. case 'f':
  1169. if (xmlStrEqual(node->name, BAD_CAST "frame"))
  1170. return(1);
  1171. return(0);
  1172. case 'h':
  1173. if (xmlStrEqual(node->name, BAD_CAST "hr"))
  1174. return(1);
  1175. return(0);
  1176. case 'i':
  1177. if (xmlStrEqual(node->name, BAD_CAST "img"))
  1178. return(1);
  1179. if (xmlStrEqual(node->name, BAD_CAST "input"))
  1180. return(1);
  1181. if (xmlStrEqual(node->name, BAD_CAST "isindex"))
  1182. return(1);
  1183. return(0);
  1184. case 'l':
  1185. if (xmlStrEqual(node->name, BAD_CAST "link"))
  1186. return(1);
  1187. return(0);
  1188. case 'm':
  1189. if (xmlStrEqual(node->name, BAD_CAST "meta"))
  1190. return(1);
  1191. return(0);
  1192. case 'p':
  1193. if (xmlStrEqual(node->name, BAD_CAST "param"))
  1194. return(1);
  1195. return(0);
  1196. }
  1197. return(0);
  1198. }
  1199. /**
  1200. * xhtmlAttrListDumpOutput:
  1201. * @cur: the first attribute pointer
  1202. *
  1203. * Dump a list of XML attributes
  1204. */
  1205. static void
  1206. xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
  1207. xmlAttrPtr xml_lang = NULL;
  1208. xmlAttrPtr lang = NULL;
  1209. xmlAttrPtr name = NULL;
  1210. xmlAttrPtr id = NULL;
  1211. xmlNodePtr parent;
  1212. xmlOutputBufferPtr buf;
  1213. if (cur == NULL) return;
  1214. buf = ctxt->buf;
  1215. parent = cur->parent;
  1216. while (cur != NULL) {
  1217. if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id")))
  1218. id = cur;
  1219. else
  1220. if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name")))
  1221. name = cur;
  1222. else
  1223. if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")))
  1224. lang = cur;
  1225. else
  1226. if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) &&
  1227. (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml")))
  1228. xml_lang = cur;
  1229. else if ((cur->ns == NULL) &&
  1230. ((cur->children == NULL) ||
  1231. (cur->children->content == NULL) ||
  1232. (cur->children->content[0] == 0)) &&
  1233. (htmlIsBooleanAttr(cur->name))) {
  1234. if (cur->children != NULL)
  1235. xmlFreeNode(cur->children);
  1236. cur->children = xmlNewDocText(cur->doc, cur->name);
  1237. if (cur->children != NULL)
  1238. cur->children->parent = (xmlNodePtr) cur;
  1239. }
  1240. xmlAttrDumpOutput(ctxt, cur);
  1241. cur = cur->next;
  1242. }
  1243. /*
  1244. * C.8
  1245. */
  1246. if ((name != NULL) && (id == NULL)) {
  1247. if ((parent != NULL) && (parent->name != NULL) &&
  1248. ((xmlStrEqual(parent->name, BAD_CAST "a")) ||
  1249. (xmlStrEqual(parent->name, BAD_CAST "p")) ||
  1250. (xmlStrEqual(parent->name, BAD_CAST "div")) ||
  1251. (xmlStrEqual(parent->name, BAD_CAST "img")) ||
  1252. (xmlStrEqual(parent->name, BAD_CAST "map")) ||
  1253. (xmlStrEqual(parent->name, BAD_CAST "applet")) ||
  1254. (xmlStrEqual(parent->name, BAD_CAST "form")) ||
  1255. (xmlStrEqual(parent->name, BAD_CAST "frame")) ||
  1256. (xmlStrEqual(parent->name, BAD_CAST "iframe")))) {
  1257. xmlOutputBufferWrite(buf, 5, " id=\"");
  1258. xmlAttrSerializeContent(buf, name);
  1259. xmlOutputBufferWrite(buf, 1, "\"");
  1260. }
  1261. }
  1262. /*
  1263. * C.7.
  1264. */
  1265. if ((lang != NULL) && (xml_lang == NULL)) {
  1266. xmlOutputBufferWrite(buf, 11, " xml:lang=\"");
  1267. xmlAttrSerializeContent(buf, lang);
  1268. xmlOutputBufferWrite(buf, 1, "\"");
  1269. } else
  1270. if ((xml_lang != NULL) && (lang == NULL)) {
  1271. xmlOutputBufferWrite(buf, 7, " lang=\"");
  1272. xmlAttrSerializeContent(buf, xml_lang);
  1273. xmlOutputBufferWrite(buf, 1, "\"");
  1274. }
  1275. }
  1276. /**
  1277. * xhtmlNodeDumpOutput:
  1278. * @buf: the XML buffer output
  1279. * @doc: the XHTML document
  1280. * @cur: the current node
  1281. * @level: the imbrication level for indenting
  1282. * @format: is formatting allowed
  1283. * @encoding: an optional encoding string
  1284. *
  1285. * Dump an XHTML node, recursive behaviour, children are printed too.
  1286. */
  1287. static void
  1288. xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
  1289. int format = ctxt->format, addmeta;
  1290. xmlNodePtr tmp, root, unformattedNode = NULL, parent;
  1291. xmlChar *start, *end;
  1292. xmlOutputBufferPtr buf = ctxt->buf;
  1293. if (cur == NULL) return;
  1294. root = cur;
  1295. parent = cur->parent;
  1296. while (1) {
  1297. switch (cur->type) {
  1298. case XML_DOCUMENT_NODE:
  1299. case XML_HTML_DOCUMENT_NODE:
  1300. xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
  1301. break;
  1302. case XML_NAMESPACE_DECL:
  1303. xmlNsDumpOutputCtxt(ctxt, (xmlNsPtr) cur);
  1304. break;
  1305. case XML_DTD_NODE:
  1306. xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
  1307. break;
  1308. case XML_DOCUMENT_FRAG_NODE:
  1309. /* Always validate cur->parent when descending. */
  1310. if ((cur->parent == parent) && (cur->children != NULL)) {
  1311. parent = cur;
  1312. cur = cur->children;
  1313. continue;
  1314. }
  1315. break;
  1316. case XML_ELEMENT_DECL:
  1317. xmlBufDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
  1318. break;
  1319. case XML_ATTRIBUTE_DECL:
  1320. xmlBufDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
  1321. break;
  1322. case XML_ENTITY_DECL:
  1323. xmlBufDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
  1324. break;
  1325. case XML_ELEMENT_NODE:
  1326. addmeta = 0;
  1327. if ((cur != root) && (ctxt->format == 1) && (xmlIndentTreeOutput))
  1328. xmlOutputBufferWrite(buf, ctxt->indent_size *
  1329. (ctxt->level > ctxt->indent_nr ?
  1330. ctxt->indent_nr : ctxt->level),
  1331. ctxt->indent);
  1332. /*
  1333. * Some users like lxml are known to pass nodes with a corrupted
  1334. * tree structure. Fall back to a recursive call to handle this
  1335. * case.
  1336. */
  1337. if ((cur->parent != parent) && (cur->children != NULL)) {
  1338. xhtmlNodeDumpOutput(ctxt, cur);
  1339. break;
  1340. }
  1341. xmlOutputBufferWrite(buf, 1, "<");
  1342. if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
  1343. xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
  1344. xmlOutputBufferWrite(buf, 1, ":");
  1345. }
  1346. xmlOutputBufferWriteString(buf, (const char *)cur->name);
  1347. if (cur->nsDef)
  1348. xmlNsListDumpOutputCtxt(ctxt, cur->nsDef);
  1349. if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
  1350. (cur->ns == NULL) && (cur->nsDef == NULL))) {
  1351. /*
  1352. * 3.1.1. Strictly Conforming Documents A.3.1.1 3/
  1353. */
  1354. xmlOutputBufferWriteString(buf,
  1355. " xmlns=\"http://www.w3.org/1999/xhtml\"");
  1356. }
  1357. if (cur->properties != NULL)
  1358. xhtmlAttrListDumpOutput(ctxt, cur->properties);
  1359. if ((parent != NULL) &&
  1360. (parent->parent == (xmlNodePtr) cur->doc) &&
  1361. xmlStrEqual(cur->name, BAD_CAST"head") &&
  1362. xmlStrEqual(parent->name, BAD_CAST"html")) {
  1363. tmp = cur->children;
  1364. while (tmp != NULL) {
  1365. if (xmlStrEqual(tmp->name, BAD_CAST"meta")) {
  1366. xmlChar *httpequiv;
  1367. httpequiv = xmlGetProp(tmp, BAD_CAST"http-equiv");
  1368. if (httpequiv != NULL) {
  1369. if (xmlStrcasecmp(httpequiv,
  1370. BAD_CAST"Content-Type") == 0) {
  1371. xmlFree(httpequiv);
  1372. break;
  1373. }
  1374. xmlFree(httpequiv);
  1375. }
  1376. }
  1377. tmp = tmp->next;
  1378. }
  1379. if (tmp == NULL)
  1380. addmeta = 1;
  1381. }
  1382. if (cur->children == NULL) {
  1383. if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) &&
  1384. ((xhtmlIsEmpty(cur) == 1) && (addmeta == 0))) {
  1385. /*
  1386. * C.2. Empty Elements
  1387. */
  1388. xmlOutputBufferWrite(buf, 3, " />");
  1389. } else {
  1390. if (addmeta == 1) {
  1391. xmlOutputBufferWrite(buf, 1, ">");
  1392. if (ctxt->format == 1) {
  1393. xmlOutputBufferWrite(buf, 1, "\n");
  1394. if (xmlIndentTreeOutput)
  1395. xmlOutputBufferWrite(buf, ctxt->indent_size *
  1396. (ctxt->level + 1 > ctxt->indent_nr ?
  1397. ctxt->indent_nr : ctxt->level + 1),
  1398. ctxt->indent);
  1399. }
  1400. xmlOutputBufferWriteString(buf,
  1401. "<meta http-equiv=\"Content-Type\" "
  1402. "content=\"text/html; charset=");
  1403. if (ctxt->encoding) {
  1404. xmlOutputBufferWriteString(buf,
  1405. (const char *)ctxt->encoding);
  1406. } else {
  1407. xmlOutputBufferWrite(buf, 5, "UTF-8");
  1408. }
  1409. xmlOutputBufferWrite(buf, 4, "\" />");
  1410. if (ctxt->format == 1)
  1411. xmlOutputBufferWrite(buf, 1, "\n");
  1412. } else {
  1413. xmlOutputBufferWrite(buf, 1, ">");
  1414. }
  1415. /*
  1416. * C.3. Element Minimization and Empty Element Content
  1417. */
  1418. xmlOutputBufferWrite(buf, 2, "</");
  1419. if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
  1420. xmlOutputBufferWriteString(buf,
  1421. (const char *)cur->ns->prefix);
  1422. xmlOutputBufferWrite(buf, 1, ":");
  1423. }
  1424. xmlOutputBufferWriteString(buf, (const char *)cur->name);
  1425. xmlOutputBufferWrite(buf, 1, ">");
  1426. }
  1427. } else {
  1428. xmlOutputBufferWrite(buf, 1, ">");
  1429. if (addmeta == 1) {
  1430. if (ctxt->format == 1) {
  1431. xmlOutputBufferWrite(buf, 1, "\n");
  1432. if (xmlIndentTreeOutput)
  1433. xmlOutputBufferWrite(buf, ctxt->indent_size *
  1434. (ctxt->level + 1 > ctxt->indent_nr ?
  1435. ctxt->indent_nr : ctxt->level + 1),
  1436. ctxt->indent);
  1437. }
  1438. xmlOutputBufferWriteString(buf,
  1439. "<meta http-equiv=\"Content-Type\" "
  1440. "content=\"text/html; charset=");
  1441. if (ctxt->encoding) {
  1442. xmlOutputBufferWriteString(buf,
  1443. (const char *)ctxt->encoding);
  1444. } else {
  1445. xmlOutputBufferWrite(buf, 5, "UTF-8");
  1446. }
  1447. xmlOutputBufferWrite(buf, 4, "\" />");
  1448. }
  1449. if (ctxt->format == 1) {
  1450. tmp = cur->children;
  1451. while (tmp != NULL) {
  1452. if ((tmp->type == XML_TEXT_NODE) ||
  1453. (tmp->type == XML_ENTITY_REF_NODE)) {
  1454. unformattedNode = cur;
  1455. ctxt->format = 0;
  1456. break;
  1457. }
  1458. tmp = tmp->next;
  1459. }
  1460. }
  1461. if (ctxt->format == 1) xmlOutputBufferWrite(buf, 1, "\n");
  1462. if (ctxt->level >= 0) ctxt->level++;
  1463. parent = cur;
  1464. cur = cur->children;
  1465. continue;
  1466. }
  1467. break;
  1468. case XML_TEXT_NODE:
  1469. if (cur->content == NULL)
  1470. break;
  1471. if ((cur->name == xmlStringText) ||
  1472. (cur->name != xmlStringTextNoenc)) {
  1473. xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
  1474. } else {
  1475. /*
  1476. * Disable escaping, needed for XSLT
  1477. */
  1478. xmlOutputBufferWriteString(buf, (const char *) cur->content);
  1479. }
  1480. break;
  1481. case XML_PI_NODE:
  1482. if (cur->content != NULL) {
  1483. xmlOutputBufferWrite(buf, 2, "<?");
  1484. xmlOutputBufferWriteString(buf, (const char *)cur->name);
  1485. if (cur->content != NULL) {
  1486. xmlOutputBufferWrite(buf, 1, " ");
  1487. xmlOutputBufferWriteString(buf,
  1488. (const char *)cur->content);
  1489. }
  1490. xmlOutputBufferWrite(buf, 2, "?>");
  1491. } else {
  1492. xmlOutputBufferWrite(buf, 2, "<?");
  1493. xmlOutputBufferWriteString(buf, (const char *)cur->name);
  1494. xmlOutputBufferWrite(buf, 2, "?>");
  1495. }
  1496. break;
  1497. case XML_COMMENT_NODE:
  1498. if (cur->content != NULL) {
  1499. xmlOutputBufferWrite(buf, 4, "<!--");
  1500. xmlOutputBufferWriteString(buf, (const char *)cur->content);
  1501. xmlOutputBufferWrite(buf, 3, "-->");
  1502. }
  1503. break;
  1504. case XML_ENTITY_REF_NODE:
  1505. xmlOutputBufferWrite(buf, 1, "&");
  1506. xmlOutputBufferWriteString(buf, (const char *)cur->name);
  1507. xmlOutputBufferWrite(buf, 1, ";");
  1508. break;
  1509. case XML_CDATA_SECTION_NODE:
  1510. if (cur->content == NULL || *cur->content == '\0') {
  1511. xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
  1512. } else {
  1513. start = end = cur->content;
  1514. while (*end != '\0') {
  1515. if (*end == ']' && *(end + 1) == ']' &&
  1516. *(end + 2) == '>') {
  1517. end = end + 2;
  1518. xmlOutputBufferWrite(buf, 9, "<![CDATA[");
  1519. xmlOutputBufferWrite(buf, end - start,
  1520. (const char *)start);
  1521. xmlOutputBufferWrite(buf, 3, "]]>");
  1522. start = end;
  1523. }
  1524. end++;
  1525. }
  1526. if (start != end) {
  1527. xmlOutputBufferWrite(buf, 9, "<![CDATA[");
  1528. xmlOutputBufferWriteString(buf, (const char *)start);
  1529. xmlOutputBufferWrite(buf, 3, "]]>");
  1530. }
  1531. }
  1532. break;
  1533. case XML_ATTRIBUTE_NODE:
  1534. xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
  1535. break;
  1536. default:
  1537. break;
  1538. }
  1539. while (1) {
  1540. if (cur == root)
  1541. return;
  1542. if (ctxt->format == 1)
  1543. xmlOutputBufferWrite(buf, 1, "\n");
  1544. if (cur->next != NULL) {
  1545. cur = cur->next;
  1546. break;
  1547. }
  1548. cur = parent;
  1549. /* cur->parent was validated when descending. */
  1550. parent = cur->parent;
  1551. if (cur->type == XML_ELEMENT_NODE) {
  1552. if (ctxt->level > 0) ctxt->level--;
  1553. if ((xmlIndentTreeOutput) && (ctxt->format == 1))
  1554. xmlOutputBufferWrite(buf, ctxt->indent_size *
  1555. (ctxt->level > ctxt->indent_nr ?
  1556. ctxt->indent_nr : ctxt->level),
  1557. ctxt->indent);
  1558. xmlOutputBufferWrite(buf, 2, "</");
  1559. if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
  1560. xmlOutputBufferWriteString(buf,
  1561. (const char *)cur->ns->prefix);
  1562. xmlOutputBufferWrite(buf, 1, ":");
  1563. }
  1564. xmlOutputBufferWriteString(buf, (const char *)cur->name);
  1565. xmlOutputBufferWrite(buf, 1, ">");
  1566. if (cur == unformattedNode) {
  1567. ctxt->format = format;
  1568. unformattedNode = NULL;
  1569. }
  1570. }
  1571. }
  1572. }
  1573. }
  1574. #endif
  1575. /************************************************************************
  1576. * *
  1577. * Public entry points *
  1578. * *
  1579. ************************************************************************/
  1580. /**
  1581. * xmlSaveToFd:
  1582. * @fd: a file descriptor number
  1583. * @encoding: the encoding name to use or NULL
  1584. * @options: a set of xmlSaveOptions
  1585. *
  1586. * Create a document saving context serializing to a file descriptor
  1587. * with the encoding and the options given.
  1588. *
  1589. * Returns a new serialization context or NULL in case of error.
  1590. */
  1591. xmlSaveCtxtPtr
  1592. xmlSaveToFd(int fd, const char *encoding, int options)
  1593. {
  1594. xmlSaveCtxtPtr ret;
  1595. ret = xmlNewSaveCtxt(encoding, options);
  1596. if (ret == NULL) return(NULL);
  1597. ret->buf = xmlOutputBufferCreateFd(fd, ret->handler);
  1598. if (ret->buf == NULL) {
  1599. xmlCharEncCloseFunc(ret->handler);
  1600. xmlFreeSaveCtxt(ret);
  1601. return(NULL);
  1602. }
  1603. return(ret);
  1604. }
  1605. /**
  1606. * xmlSaveToFilename:
  1607. * @filename: a file name or an URL
  1608. * @encoding: the encoding name to use or NULL
  1609. * @options: a set of xmlSaveOptions
  1610. *
  1611. * Create a document saving context serializing to a filename or possibly
  1612. * to an URL (but this is less reliable) with the encoding and the options
  1613. * given.
  1614. *
  1615. * Returns a new serialization context or NULL in case of error.
  1616. */
  1617. xmlSaveCtxtPtr
  1618. xmlSaveToFilename(const char *filename, const char *encoding, int options)
  1619. {
  1620. xmlSaveCtxtPtr ret;
  1621. int compression = 0; /* TODO handle compression option */
  1622. ret = xmlNewSaveCtxt(encoding, options);
  1623. if (ret == NULL) return(NULL);
  1624. ret->buf = xmlOutputBufferCreateFilename(filename, ret->handler,
  1625. compression);
  1626. if (ret->buf == NULL) {
  1627. xmlCharEncCloseFunc(ret->handler);
  1628. xmlFreeSaveCtxt(ret);
  1629. return(NULL);
  1630. }
  1631. return(ret);
  1632. }
  1633. /**
  1634. * xmlSaveToBuffer:
  1635. * @buffer: a buffer
  1636. * @encoding: the encoding name to use or NULL
  1637. * @options: a set of xmlSaveOptions
  1638. *
  1639. * Create a document saving context serializing to a buffer
  1640. * with the encoding and the options given
  1641. *
  1642. * Returns a new serialization context or NULL in case of error.
  1643. */
  1644. xmlSaveCtxtPtr
  1645. xmlSaveToBuffer(xmlBufferPtr buffer, const char *encoding, int options)
  1646. {
  1647. xmlSaveCtxtPtr ret;
  1648. ret = xmlNewSaveCtxt(encoding, options);
  1649. if (ret == NULL) return(NULL);
  1650. ret->buf = xmlOutputBufferCreateBuffer(buffer, ret->handler);
  1651. if (ret->buf == NULL) {
  1652. xmlCharEncCloseFunc(ret->handler);
  1653. xmlFreeSaveCtxt(ret);
  1654. return(NULL);
  1655. }
  1656. return(ret);
  1657. }
  1658. /**
  1659. * xmlSaveToIO:
  1660. * @iowrite: an I/O write function
  1661. * @ioclose: an I/O close function
  1662. * @ioctx: an I/O handler
  1663. * @encoding: the encoding name to use or NULL
  1664. * @options: a set of xmlSaveOptions
  1665. *
  1666. * Create a document saving context serializing to a file descriptor
  1667. * with the encoding and the options given
  1668. *
  1669. * Returns a new serialization context or NULL in case of error.
  1670. */
  1671. xmlSaveCtxtPtr
  1672. xmlSaveToIO(xmlOutputWriteCallback iowrite,
  1673. xmlOutputCloseCallback ioclose,
  1674. void *ioctx, const char *encoding, int options)
  1675. {
  1676. xmlSaveCtxtPtr ret;
  1677. ret = xmlNewSaveCtxt(encoding, options);
  1678. if (ret == NULL) return(NULL);
  1679. ret->buf = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler);
  1680. if (ret->buf == NULL) {
  1681. xmlCharEncCloseFunc(ret->handler);
  1682. xmlFreeSaveCtxt(ret);
  1683. return(NULL);
  1684. }
  1685. return(ret);
  1686. }
  1687. /**
  1688. * xmlSaveDoc:
  1689. * @ctxt: a document saving context
  1690. * @doc: a document
  1691. *
  1692. * Save a full document to a saving context
  1693. * TODO: The function is not fully implemented yet as it does not return the
  1694. * byte count but 0 instead
  1695. *
  1696. * Returns the number of byte written or -1 in case of error
  1697. */
  1698. long
  1699. xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc)
  1700. {
  1701. long ret = 0;
  1702. if ((ctxt == NULL) || (doc == NULL)) return(-1);
  1703. if (xmlDocContentDumpOutput(ctxt, doc) < 0)
  1704. return(-1);
  1705. return(ret);
  1706. }
  1707. /**
  1708. * xmlSaveTree:
  1709. * @ctxt: a document saving context
  1710. * @cur: the top node of the subtree to save
  1711. *
  1712. * Save a subtree starting at the node parameter to a saving context
  1713. * TODO: The function is not fully implemented yet as it does not return the
  1714. * byte count but 0 instead
  1715. *
  1716. * Returns the number of byte written or -1 in case of error
  1717. */
  1718. long
  1719. xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr cur)
  1720. {
  1721. long ret = 0;
  1722. if ((ctxt == NULL) || (cur == NULL)) return(-1);
  1723. #ifdef LIBXML_HTML_ENABLED
  1724. if (ctxt->options & XML_SAVE_XHTML) {
  1725. xhtmlNodeDumpOutput(ctxt, cur);
  1726. return(ret);
  1727. }
  1728. if (((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL) &&
  1729. (cur->doc->type == XML_HTML_DOCUMENT_NODE) &&
  1730. ((ctxt->options & XML_SAVE_AS_XML) == 0)) ||
  1731. (ctxt->options & XML_SAVE_AS_HTML)) {
  1732. htmlNodeDumpOutputInternal(ctxt, cur);
  1733. return(ret);
  1734. }
  1735. #endif
  1736. xmlNodeDumpOutputInternal(ctxt, cur);
  1737. return(ret);
  1738. }
  1739. /**
  1740. * xmlSaveFlush:
  1741. * @ctxt: a document saving context
  1742. *
  1743. * Flush a document saving context, i.e. make sure that all bytes have
  1744. * been output.
  1745. *
  1746. * Returns the number of byte written or -1 in case of error.
  1747. */
  1748. int
  1749. xmlSaveFlush(xmlSaveCtxtPtr ctxt)
  1750. {
  1751. if (ctxt == NULL) return(-1);
  1752. if (ctxt->buf == NULL) return(-1);
  1753. return(xmlOutputBufferFlush(ctxt->buf));
  1754. }
  1755. /**
  1756. * xmlSaveClose:
  1757. * @ctxt: a document saving context
  1758. *
  1759. * Close a document saving context, i.e. make sure that all bytes have
  1760. * been output and free the associated data.
  1761. *
  1762. * Returns the number of byte written or -1 in case of error.
  1763. */
  1764. int
  1765. xmlSaveClose(xmlSaveCtxtPtr ctxt)
  1766. {
  1767. int ret;
  1768. if (ctxt == NULL) return(-1);
  1769. ret = xmlSaveFlush(ctxt);
  1770. xmlFreeSaveCtxt(ctxt);
  1771. return(ret);
  1772. }
  1773. /**
  1774. * xmlSaveSetEscape:
  1775. * @ctxt: a document saving context
  1776. * @escape: the escaping function
  1777. *
  1778. * Set a custom escaping function to be used for text in element content
  1779. *
  1780. * Returns 0 if successful or -1 in case of error.
  1781. */
  1782. int
  1783. xmlSaveSetEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
  1784. {
  1785. if (ctxt == NULL) return(-1);
  1786. ctxt->escape = escape;
  1787. return(0);
  1788. }
  1789. /**
  1790. * xmlSaveSetAttrEscape:
  1791. * @ctxt: a document saving context
  1792. * @escape: the escaping function
  1793. *
  1794. * Set a custom escaping function to be used for text in attribute content
  1795. *
  1796. * Returns 0 if successful or -1 in case of error.
  1797. */
  1798. int
  1799. xmlSaveSetAttrEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
  1800. {
  1801. if (ctxt == NULL) return(-1);
  1802. ctxt->escapeAttr = escape;
  1803. return(0);
  1804. }
  1805. /************************************************************************
  1806. * *
  1807. * Public entry points based on buffers *
  1808. * *
  1809. ************************************************************************/
  1810. /**
  1811. * xmlBufAttrSerializeTxtContent:
  1812. * @buf: and xmlBufPtr output
  1813. * @doc: the document
  1814. * @attr: the attribute node
  1815. * @string: the text content
  1816. *
  1817. * Serialize text attribute values to an xmlBufPtr
  1818. */
  1819. void
  1820. xmlBufAttrSerializeTxtContent(xmlBufPtr buf, xmlDocPtr doc,
  1821. xmlAttrPtr attr, const xmlChar * string)
  1822. {
  1823. xmlChar *base, *cur;
  1824. if (string == NULL)
  1825. return;
  1826. base = cur = (xmlChar *) string;
  1827. while (*cur != 0) {
  1828. if (*cur == '\n') {
  1829. if (base != cur)
  1830. xmlBufAdd(buf, base, cur - base);
  1831. xmlBufAdd(buf, BAD_CAST "&#10;", 5);
  1832. cur++;
  1833. base = cur;
  1834. } else if (*cur == '\r') {
  1835. if (base != cur)
  1836. xmlBufAdd(buf, base, cur - base);
  1837. xmlBufAdd(buf, BAD_CAST "&#13;", 5);
  1838. cur++;
  1839. base = cur;
  1840. } else if (*cur == '\t') {
  1841. if (base != cur)
  1842. xmlBufAdd(buf, base, cur - base);
  1843. xmlBufAdd(buf, BAD_CAST "&#9;", 4);
  1844. cur++;
  1845. base = cur;
  1846. } else if (*cur == '"') {
  1847. if (base != cur)
  1848. xmlBufAdd(buf, base, cur - base);
  1849. xmlBufAdd(buf, BAD_CAST "&quot;", 6);
  1850. cur++;
  1851. base = cur;
  1852. } else if (*cur == '<') {
  1853. if (base != cur)
  1854. xmlBufAdd(buf, base, cur - base);
  1855. xmlBufAdd(buf, BAD_CAST "&lt;", 4);
  1856. cur++;
  1857. base = cur;
  1858. } else if (*cur == '>') {
  1859. if (base != cur)
  1860. xmlBufAdd(buf, base, cur - base);
  1861. xmlBufAdd(buf, BAD_CAST "&gt;", 4);
  1862. cur++;
  1863. base = cur;
  1864. } else if (*cur == '&') {
  1865. if (base != cur)
  1866. xmlBufAdd(buf, base, cur - base);
  1867. xmlBufAdd(buf, BAD_CAST "&amp;", 5);
  1868. cur++;
  1869. base = cur;
  1870. } else if ((*cur >= 0x80) && (cur[1] != 0) &&
  1871. ((doc == NULL) || (doc->encoding == NULL))) {
  1872. /*
  1873. * We assume we have UTF-8 content.
  1874. */
  1875. unsigned char tmp[12];
  1876. int val = 0, l = 1;
  1877. if (base != cur)
  1878. xmlBufAdd(buf, base, cur - base);
  1879. if (*cur < 0xC0) {
  1880. xmlSaveErr(XML_SAVE_NOT_UTF8, (xmlNodePtr) attr, NULL);
  1881. xmlSerializeHexCharRef(tmp, *cur);
  1882. xmlBufAdd(buf, (xmlChar *) tmp, -1);
  1883. cur++;
  1884. base = cur;
  1885. continue;
  1886. } else if (*cur < 0xE0) {
  1887. val = (cur[0]) & 0x1F;
  1888. val <<= 6;
  1889. val |= (cur[1]) & 0x3F;
  1890. l = 2;
  1891. } else if ((*cur < 0xF0) && (cur [2] != 0)) {
  1892. val = (cur[0]) & 0x0F;
  1893. val <<= 6;
  1894. val |= (cur[1]) & 0x3F;
  1895. val <<= 6;
  1896. val |= (cur[2]) & 0x3F;
  1897. l = 3;
  1898. } else if ((*cur < 0xF8) && (cur [2] != 0) && (cur[3] != 0)) {
  1899. val = (cur[0]) & 0x07;
  1900. val <<= 6;
  1901. val |= (cur[1]) & 0x3F;
  1902. val <<= 6;
  1903. val |= (cur[2]) & 0x3F;
  1904. val <<= 6;
  1905. val |= (cur[3]) & 0x3F;
  1906. l = 4;
  1907. }
  1908. if ((l == 1) || (!IS_CHAR(val))) {
  1909. xmlSaveErr(XML_SAVE_CHAR_INVALID, (xmlNodePtr) attr, NULL);
  1910. xmlSerializeHexCharRef(tmp, *cur);
  1911. xmlBufAdd(buf, (xmlChar *) tmp, -1);
  1912. cur++;
  1913. base = cur;
  1914. continue;
  1915. }
  1916. /*
  1917. * We could do multiple things here. Just save
  1918. * as a char ref
  1919. */
  1920. xmlSerializeHexCharRef(tmp, val);
  1921. xmlBufAdd(buf, (xmlChar *) tmp, -1);
  1922. cur += l;
  1923. base = cur;
  1924. } else {
  1925. cur++;
  1926. }
  1927. }
  1928. if (base != cur)
  1929. xmlBufAdd(buf, base, cur - base);
  1930. }
  1931. /**
  1932. * xmlAttrSerializeTxtContent:
  1933. * @buf: the XML buffer output
  1934. * @doc: the document
  1935. * @attr: the attribute node
  1936. * @string: the text content
  1937. *
  1938. * Serialize text attribute values to an xml simple buffer
  1939. */
  1940. void
  1941. xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc,
  1942. xmlAttrPtr attr, const xmlChar * string)
  1943. {
  1944. xmlBufPtr buffer;
  1945. if ((buf == NULL) || (string == NULL))
  1946. return;
  1947. buffer = xmlBufFromBuffer(buf);
  1948. if (buffer == NULL)
  1949. return;
  1950. xmlBufAttrSerializeTxtContent(buffer, doc, attr, string);
  1951. xmlBufBackToBuffer(buffer);
  1952. }
  1953. /**
  1954. * xmlNodeDump:
  1955. * @buf: the XML buffer output
  1956. * @doc: the document
  1957. * @cur: the current node
  1958. * @level: the imbrication level for indenting
  1959. * @format: is formatting allowed
  1960. *
  1961. * Dump an XML node, recursive behaviour,children are printed too.
  1962. * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
  1963. * or xmlKeepBlanksDefault(0) was called.
  1964. * Since this is using xmlBuffer structures it is limited to 2GB and somehow
  1965. * deprecated, use xmlNodeDumpOutput() instead.
  1966. *
  1967. * Returns the number of bytes written to the buffer or -1 in case of error
  1968. */
  1969. int
  1970. xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
  1971. int format)
  1972. {
  1973. xmlBufPtr buffer;
  1974. size_t ret;
  1975. if ((buf == NULL) || (cur == NULL))
  1976. return(-1);
  1977. buffer = xmlBufFromBuffer(buf);
  1978. if (buffer == NULL)
  1979. return(-1);
  1980. ret = xmlBufNodeDump(buffer, doc, cur, level, format);
  1981. xmlBufBackToBuffer(buffer);
  1982. if (ret > INT_MAX)
  1983. return(-1);
  1984. return(ret);
  1985. }
  1986. /**
  1987. * xmlBufNodeDump:
  1988. * @buf: the XML buffer output
  1989. * @doc: the document
  1990. * @cur: the current node
  1991. * @level: the imbrication level for indenting
  1992. * @format: is formatting allowed
  1993. *
  1994. * Dump an XML node, recursive behaviour,children are printed too.
  1995. * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
  1996. * or xmlKeepBlanksDefault(0) was called
  1997. *
  1998. * Returns the number of bytes written to the buffer, in case of error 0
  1999. * is returned or @buf stores the error
  2000. */
  2001. size_t
  2002. xmlBufNodeDump(xmlBufPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
  2003. int format)
  2004. {
  2005. size_t use;
  2006. int ret;
  2007. xmlOutputBufferPtr outbuf;
  2008. int oldalloc;
  2009. xmlInitParser();
  2010. if (cur == NULL) {
  2011. return (-1);
  2012. }
  2013. if (buf == NULL) {
  2014. return (-1);
  2015. }
  2016. outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
  2017. if (outbuf == NULL) {
  2018. xmlSaveErrMemory("creating buffer");
  2019. return (-1);
  2020. }
  2021. memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
  2022. outbuf->buffer = buf;
  2023. outbuf->encoder = NULL;
  2024. outbuf->writecallback = NULL;
  2025. outbuf->closecallback = NULL;
  2026. outbuf->context = NULL;
  2027. outbuf->written = 0;
  2028. use = xmlBufUse(buf);
  2029. oldalloc = xmlBufGetAllocationScheme(buf);
  2030. xmlBufSetAllocationScheme(buf, XML_BUFFER_ALLOC_DOUBLEIT);
  2031. xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL);
  2032. xmlBufSetAllocationScheme(buf, oldalloc);
  2033. xmlFree(outbuf);
  2034. ret = xmlBufUse(buf) - use;
  2035. return (ret);
  2036. }
  2037. /**
  2038. * xmlElemDump:
  2039. * @f: the FILE * for the output
  2040. * @doc: the document
  2041. * @cur: the current node
  2042. *
  2043. * Dump an XML/HTML node, recursive behaviour, children are printed too.
  2044. */
  2045. void
  2046. xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur)
  2047. {
  2048. xmlOutputBufferPtr outbuf;
  2049. xmlInitParser();
  2050. if (cur == NULL) {
  2051. return;
  2052. }
  2053. outbuf = xmlOutputBufferCreateFile(f, NULL);
  2054. if (outbuf == NULL)
  2055. return;
  2056. if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) {
  2057. #ifdef LIBXML_HTML_ENABLED
  2058. htmlNodeDumpOutput(outbuf, doc, cur, NULL);
  2059. #else
  2060. xmlSaveErr(XML_ERR_INTERNAL_ERROR, cur, "HTML support not compiled in\n");
  2061. #endif /* LIBXML_HTML_ENABLED */
  2062. } else
  2063. xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL);
  2064. xmlOutputBufferClose(outbuf);
  2065. }
  2066. /************************************************************************
  2067. * *
  2068. * Saving functions front-ends *
  2069. * *
  2070. ************************************************************************/
  2071. /**
  2072. * xmlNodeDumpOutput:
  2073. * @buf: the XML buffer output
  2074. * @doc: the document
  2075. * @cur: the current node
  2076. * @level: the imbrication level for indenting
  2077. * @format: is formatting allowed
  2078. * @encoding: an optional encoding string
  2079. *
  2080. * Dump an XML node, recursive behaviour, children are printed too.
  2081. * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
  2082. * or xmlKeepBlanksDefault(0) was called
  2083. */
  2084. void
  2085. xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
  2086. int level, int format, const char *encoding)
  2087. {
  2088. xmlSaveCtxt ctxt;
  2089. #ifdef LIBXML_HTML_ENABLED
  2090. xmlDtdPtr dtd;
  2091. int is_xhtml = 0;
  2092. #endif
  2093. (void) doc;
  2094. xmlInitParser();
  2095. if ((buf == NULL) || (cur == NULL)) return;
  2096. if (encoding == NULL)
  2097. encoding = "UTF-8";
  2098. memset(&ctxt, 0, sizeof(ctxt));
  2099. ctxt.buf = buf;
  2100. ctxt.level = level;
  2101. ctxt.format = format ? 1 : 0;
  2102. ctxt.encoding = (const xmlChar *) encoding;
  2103. xmlSaveCtxtInit(&ctxt);
  2104. ctxt.options |= XML_SAVE_AS_XML;
  2105. #ifdef LIBXML_HTML_ENABLED
  2106. dtd = xmlGetIntSubset(doc);
  2107. if (dtd != NULL) {
  2108. is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
  2109. if (is_xhtml < 0)
  2110. is_xhtml = 0;
  2111. }
  2112. if (is_xhtml)
  2113. xhtmlNodeDumpOutput(&ctxt, cur);
  2114. else
  2115. #endif
  2116. xmlNodeDumpOutputInternal(&ctxt, cur);
  2117. }
  2118. /**
  2119. * xmlDocDumpFormatMemoryEnc:
  2120. * @out_doc: Document to generate XML text from
  2121. * @doc_txt_ptr: Memory pointer for allocated XML text
  2122. * @doc_txt_len: Length of the generated XML text
  2123. * @txt_encoding: Character encoding to use when generating XML text
  2124. * @format: should formatting spaces been added
  2125. *
  2126. * Dump the current DOM tree into memory using the character encoding specified
  2127. * by the caller. Note it is up to the caller of this function to free the
  2128. * allocated memory with xmlFree().
  2129. * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
  2130. * or xmlKeepBlanksDefault(0) was called
  2131. */
  2132. void
  2133. xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
  2134. int * doc_txt_len, const char * txt_encoding,
  2135. int format) {
  2136. xmlSaveCtxt ctxt;
  2137. int dummy = 0;
  2138. xmlOutputBufferPtr out_buff = NULL;
  2139. xmlCharEncodingHandlerPtr conv_hdlr = NULL;
  2140. if (doc_txt_len == NULL) {
  2141. doc_txt_len = &dummy; /* Continue, caller just won't get length */
  2142. }
  2143. if (doc_txt_ptr == NULL) {
  2144. *doc_txt_len = 0;
  2145. return;
  2146. }
  2147. *doc_txt_ptr = NULL;
  2148. *doc_txt_len = 0;
  2149. if (out_doc == NULL) {
  2150. /* No document, no output */
  2151. return;
  2152. }
  2153. /*
  2154. * Validate the encoding value, if provided.
  2155. * This logic is copied from xmlSaveFileEnc.
  2156. */
  2157. if (txt_encoding == NULL)
  2158. txt_encoding = (const char *) out_doc->encoding;
  2159. if (txt_encoding != NULL) {
  2160. conv_hdlr = xmlFindCharEncodingHandler(txt_encoding);
  2161. if ( conv_hdlr == NULL ) {
  2162. xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, (xmlNodePtr) out_doc,
  2163. txt_encoding);
  2164. return;
  2165. }
  2166. }
  2167. if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) {
  2168. xmlSaveErrMemory("creating buffer");
  2169. xmlCharEncCloseFunc(conv_hdlr);
  2170. return;
  2171. }
  2172. memset(&ctxt, 0, sizeof(ctxt));
  2173. ctxt.buf = out_buff;
  2174. ctxt.level = 0;
  2175. ctxt.format = format ? 1 : 0;
  2176. ctxt.encoding = (const xmlChar *) txt_encoding;
  2177. xmlSaveCtxtInit(&ctxt);
  2178. ctxt.options |= XML_SAVE_AS_XML;
  2179. xmlDocContentDumpOutput(&ctxt, out_doc);
  2180. xmlOutputBufferFlush(out_buff);
  2181. if (out_buff->conv != NULL) {
  2182. *doc_txt_len = xmlBufUse(out_buff->conv);
  2183. *doc_txt_ptr = xmlStrndup(xmlBufContent(out_buff->conv), *doc_txt_len);
  2184. } else {
  2185. *doc_txt_len = xmlBufUse(out_buff->buffer);
  2186. *doc_txt_ptr = xmlStrndup(xmlBufContent(out_buff->buffer),*doc_txt_len);
  2187. }
  2188. (void)xmlOutputBufferClose(out_buff);
  2189. if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) {
  2190. *doc_txt_len = 0;
  2191. xmlSaveErrMemory("creating output");
  2192. }
  2193. return;
  2194. }
  2195. /**
  2196. * xmlDocDumpMemory:
  2197. * @cur: the document
  2198. * @mem: OUT: the memory pointer
  2199. * @size: OUT: the memory length
  2200. *
  2201. * Dump an XML document in memory and return the #xmlChar * and it's size
  2202. * in bytes. It's up to the caller to free the memory with xmlFree().
  2203. * The resulting byte array is zero terminated, though the last 0 is not
  2204. * included in the returned size.
  2205. */
  2206. void
  2207. xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
  2208. xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
  2209. }
  2210. /**
  2211. * xmlDocDumpFormatMemory:
  2212. * @cur: the document
  2213. * @mem: OUT: the memory pointer
  2214. * @size: OUT: the memory length
  2215. * @format: should formatting spaces been added
  2216. *
  2217. *
  2218. * Dump an XML document in memory and return the #xmlChar * and it's size.
  2219. * It's up to the caller to free the memory with xmlFree().
  2220. * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
  2221. * or xmlKeepBlanksDefault(0) was called
  2222. */
  2223. void
  2224. xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
  2225. xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
  2226. }
  2227. /**
  2228. * xmlDocDumpMemoryEnc:
  2229. * @out_doc: Document to generate XML text from
  2230. * @doc_txt_ptr: Memory pointer for allocated XML text
  2231. * @doc_txt_len: Length of the generated XML text
  2232. * @txt_encoding: Character encoding to use when generating XML text
  2233. *
  2234. * Dump the current DOM tree into memory using the character encoding specified
  2235. * by the caller. Note it is up to the caller of this function to free the
  2236. * allocated memory with xmlFree().
  2237. */
  2238. void
  2239. xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
  2240. int * doc_txt_len, const char * txt_encoding) {
  2241. xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
  2242. txt_encoding, 0);
  2243. }
  2244. /**
  2245. * xmlDocFormatDump:
  2246. * @f: the FILE*
  2247. * @cur: the document
  2248. * @format: should formatting spaces been added
  2249. *
  2250. * Dump an XML document to an open FILE.
  2251. *
  2252. * returns: the number of bytes written or -1 in case of failure.
  2253. * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
  2254. * or xmlKeepBlanksDefault(0) was called
  2255. */
  2256. int
  2257. xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
  2258. xmlSaveCtxt ctxt;
  2259. xmlOutputBufferPtr buf;
  2260. const char * encoding;
  2261. xmlCharEncodingHandlerPtr handler = NULL;
  2262. int ret;
  2263. if (cur == NULL) {
  2264. return(-1);
  2265. }
  2266. encoding = (const char *) cur->encoding;
  2267. if (encoding != NULL) {
  2268. handler = xmlFindCharEncodingHandler(encoding);
  2269. if (handler == NULL) {
  2270. xmlFree((char *) cur->encoding);
  2271. cur->encoding = NULL;
  2272. encoding = NULL;
  2273. }
  2274. }
  2275. buf = xmlOutputBufferCreateFile(f, handler);
  2276. if (buf == NULL) return(-1);
  2277. memset(&ctxt, 0, sizeof(ctxt));
  2278. ctxt.buf = buf;
  2279. ctxt.level = 0;
  2280. ctxt.format = format ? 1 : 0;
  2281. ctxt.encoding = (const xmlChar *) encoding;
  2282. xmlSaveCtxtInit(&ctxt);
  2283. ctxt.options |= XML_SAVE_AS_XML;
  2284. xmlDocContentDumpOutput(&ctxt, cur);
  2285. ret = xmlOutputBufferClose(buf);
  2286. return(ret);
  2287. }
  2288. /**
  2289. * xmlDocDump:
  2290. * @f: the FILE*
  2291. * @cur: the document
  2292. *
  2293. * Dump an XML document to an open FILE.
  2294. *
  2295. * returns: the number of bytes written or -1 in case of failure.
  2296. */
  2297. int
  2298. xmlDocDump(FILE *f, xmlDocPtr cur) {
  2299. return(xmlDocFormatDump (f, cur, 0));
  2300. }
  2301. /**
  2302. * xmlSaveFileTo:
  2303. * @buf: an output I/O buffer
  2304. * @cur: the document
  2305. * @encoding: the encoding if any assuming the I/O layer handles the transcoding
  2306. *
  2307. * Dump an XML document to an I/O buffer.
  2308. * Warning ! This call xmlOutputBufferClose() on buf which is not available
  2309. * after this call.
  2310. *
  2311. * returns: the number of bytes written or -1 in case of failure.
  2312. */
  2313. int
  2314. xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
  2315. xmlSaveCtxt ctxt;
  2316. int ret;
  2317. if (buf == NULL) return(-1);
  2318. if (cur == NULL) {
  2319. xmlOutputBufferClose(buf);
  2320. return(-1);
  2321. }
  2322. memset(&ctxt, 0, sizeof(ctxt));
  2323. ctxt.buf = buf;
  2324. ctxt.level = 0;
  2325. ctxt.format = 0;
  2326. ctxt.encoding = (const xmlChar *) encoding;
  2327. xmlSaveCtxtInit(&ctxt);
  2328. ctxt.options |= XML_SAVE_AS_XML;
  2329. xmlDocContentDumpOutput(&ctxt, cur);
  2330. ret = xmlOutputBufferClose(buf);
  2331. return(ret);
  2332. }
  2333. /**
  2334. * xmlSaveFormatFileTo:
  2335. * @buf: an output I/O buffer
  2336. * @cur: the document
  2337. * @encoding: the encoding if any assuming the I/O layer handles the transcoding
  2338. * @format: should formatting spaces been added
  2339. *
  2340. * Dump an XML document to an I/O buffer.
  2341. * Warning ! This call xmlOutputBufferClose() on buf which is not available
  2342. * after this call.
  2343. *
  2344. * returns: the number of bytes written or -1 in case of failure.
  2345. */
  2346. int
  2347. xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
  2348. const char *encoding, int format)
  2349. {
  2350. xmlSaveCtxt ctxt;
  2351. int ret;
  2352. if (buf == NULL) return(-1);
  2353. if ((cur == NULL) ||
  2354. ((cur->type != XML_DOCUMENT_NODE) &&
  2355. (cur->type != XML_HTML_DOCUMENT_NODE))) {
  2356. xmlOutputBufferClose(buf);
  2357. return(-1);
  2358. }
  2359. memset(&ctxt, 0, sizeof(ctxt));
  2360. ctxt.buf = buf;
  2361. ctxt.level = 0;
  2362. ctxt.format = format ? 1 : 0;
  2363. ctxt.encoding = (const xmlChar *) encoding;
  2364. xmlSaveCtxtInit(&ctxt);
  2365. ctxt.options |= XML_SAVE_AS_XML;
  2366. xmlDocContentDumpOutput(&ctxt, cur);
  2367. ret = xmlOutputBufferClose(buf);
  2368. return (ret);
  2369. }
  2370. /**
  2371. * xmlSaveFormatFileEnc:
  2372. * @filename: the filename or URL to output
  2373. * @cur: the document being saved
  2374. * @encoding: the name of the encoding to use or NULL.
  2375. * @format: should formatting spaces be added.
  2376. *
  2377. * Dump an XML document to a file or an URL.
  2378. *
  2379. * Returns the number of bytes written or -1 in case of error.
  2380. * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
  2381. * or xmlKeepBlanksDefault(0) was called
  2382. */
  2383. int
  2384. xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
  2385. const char * encoding, int format ) {
  2386. xmlSaveCtxt ctxt;
  2387. xmlOutputBufferPtr buf;
  2388. xmlCharEncodingHandlerPtr handler = NULL;
  2389. int ret;
  2390. if (cur == NULL)
  2391. return(-1);
  2392. if (encoding == NULL)
  2393. encoding = (const char *) cur->encoding;
  2394. if (encoding != NULL) {
  2395. handler = xmlFindCharEncodingHandler(encoding);
  2396. if (handler == NULL)
  2397. return(-1);
  2398. }
  2399. #ifdef LIBXML_ZLIB_ENABLED
  2400. if (cur->compression < 0) cur->compression = xmlGetCompressMode();
  2401. #endif
  2402. /*
  2403. * save the content to a temp buffer.
  2404. */
  2405. buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
  2406. if (buf == NULL) return(-1);
  2407. memset(&ctxt, 0, sizeof(ctxt));
  2408. ctxt.buf = buf;
  2409. ctxt.level = 0;
  2410. ctxt.format = format ? 1 : 0;
  2411. ctxt.encoding = (const xmlChar *) encoding;
  2412. xmlSaveCtxtInit(&ctxt);
  2413. ctxt.options |= XML_SAVE_AS_XML;
  2414. xmlDocContentDumpOutput(&ctxt, cur);
  2415. ret = xmlOutputBufferClose(buf);
  2416. return(ret);
  2417. }
  2418. /**
  2419. * xmlSaveFileEnc:
  2420. * @filename: the filename (or URL)
  2421. * @cur: the document
  2422. * @encoding: the name of an encoding (or NULL)
  2423. *
  2424. * Dump an XML document, converting it to the given encoding
  2425. *
  2426. * returns: the number of bytes written or -1 in case of failure.
  2427. */
  2428. int
  2429. xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
  2430. return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
  2431. }
  2432. /**
  2433. * xmlSaveFormatFile:
  2434. * @filename: the filename (or URL)
  2435. * @cur: the document
  2436. * @format: should formatting spaces been added
  2437. *
  2438. * Dump an XML document to a file. Will use compression if
  2439. * compiled in and enabled. If @filename is "-" the stdout file is
  2440. * used. If @format is set then the document will be indented on output.
  2441. * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
  2442. * or xmlKeepBlanksDefault(0) was called
  2443. *
  2444. * returns: the number of bytes written or -1 in case of failure.
  2445. */
  2446. int
  2447. xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
  2448. return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
  2449. }
  2450. /**
  2451. * xmlSaveFile:
  2452. * @filename: the filename (or URL)
  2453. * @cur: the document
  2454. *
  2455. * Dump an XML document to a file. Will use compression if
  2456. * compiled in and enabled. If @filename is "-" the stdout file is
  2457. * used.
  2458. * returns: the number of bytes written or -1 in case of failure.
  2459. */
  2460. int
  2461. xmlSaveFile(const char *filename, xmlDocPtr cur) {
  2462. return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
  2463. }
  2464. #endif /* LIBXML_OUTPUT_ENABLED */