Happy new year
[idzebra-moved-to-github.git] / data1 / d1_write.c
1 /* This file is part of the Zebra server.
2    Copyright (C) 1994-2009 Index Data
3
4 Zebra is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
7 version.
8
9 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
18 */
19
20 /* converts data1 tree to XML record */
21
22 #include <string.h>
23
24 #include <idzebra/data1.h>
25 #include <yaz/wrbuf.h>
26
27 #define IDSGML_MARGIN 75
28
29 #define PRETTY_FORMAT 0
30
31 static int wordlen(char *b, int max)
32 {
33     int l = 0;
34
35     while (l < max && !d1_isspace(*b))
36         l++, b++;
37     return l;
38 }
39
40 static void indent (WRBUF b, int col)
41 {
42     int i;
43     for (i = 0; i<col; i++)
44         wrbuf_putc (b, ' ');
45 }
46
47 static void wrbuf_put_xattr(WRBUF b, data1_xattr *p)
48 {
49     for (; p; p = p->next)
50     {
51         wrbuf_putc (b, ' ');
52         if (p->what == DATA1I_xmltext)
53             wrbuf_puts (b, p->name);
54         else
55             wrbuf_xmlputs (b, p->name);
56         if (p->value)
57         {
58             wrbuf_putc (b, '=');
59             wrbuf_putc (b, '"');
60             if (p->what == DATA1I_text)
61                 wrbuf_xmlputs (b, p->value);
62             else
63                 wrbuf_puts (b, p->value);
64             wrbuf_putc (b, '"');
65         }
66     }
67 }
68
69 static void wrbuf_write_tag(WRBUF b, const char *tag, int opening)
70 {
71     int i, fixup = 0;
72
73     /* see if we must fix the tag.. The grs.marc filter produces
74        a data1 tree with not well-formed XML */
75     if (*tag >= '0' && *tag <= '9')
76         fixup = 1;
77     for (i = 0; tag[i]; i++)
78         if (strchr( " <>$,()[]", tag[i]))
79             fixup = 1;
80     if (fixup)
81     {
82         wrbuf_puts(b, "tag");
83         if (opening)
84         {
85             wrbuf_puts(b, " value=\"");
86             wrbuf_xmlputs(b, tag);
87             wrbuf_puts(b, "\"");
88         }
89     }
90     else
91         wrbuf_puts(b, tag);
92 }
93
94 static int nodetoidsgml(data1_node *n, int select, WRBUF b, int col,
95                         int pretty_format)
96 {
97     data1_node *c;
98
99     for (c = n->child; c; c = c->next)
100     {
101         char *tag;
102
103         if (c->which == DATA1N_preprocess)
104         {
105             if (pretty_format)
106                 indent (b, col);
107             wrbuf_puts (b, "<?");
108             wrbuf_xmlputs (b, c->u.preprocess.target);
109             wrbuf_put_xattr (b, c->u.preprocess.attributes);
110             if (c->child)
111                 wrbuf_puts(b, " ");
112             if (nodetoidsgml(c, select, b, (col > 40) ? 40 : col+2,
113                              pretty_format) < 0)
114                 return -1;
115             wrbuf_puts (b, "?>\n");
116         }
117         else if (c->which == DATA1N_tag)
118         {
119             if (select && !c->u.tag.node_selected)
120                 continue;
121             tag = c->u.tag.tag;
122             if (!data1_matchstr(tag, "wellknown")) /* skip wellknown */
123             {
124                 if (nodetoidsgml(c, select, b, col, pretty_format) < 0)
125                     return -1;
126             }
127             else
128             {
129                 if (pretty_format)
130                     indent (b, col);
131                 wrbuf_puts(b, "<");
132                 wrbuf_write_tag(b, tag, 1);
133                 wrbuf_put_xattr (b, c->u.tag.attributes);
134                 wrbuf_puts(b, ">");
135                 if (pretty_format)
136                     wrbuf_puts(b, "\n");
137                 if (nodetoidsgml(c, select, b, (col > 40) ? 40 : col+2,
138                                  pretty_format) < 0)
139                     return -1;
140                 if (pretty_format)
141                     indent (b, col);
142                 wrbuf_puts(b, "</");
143                 wrbuf_write_tag(b, tag, 0);
144                 wrbuf_puts(b, ">");
145                 if (pretty_format)
146                     wrbuf_puts (b, "\n");
147             }
148         }
149         else if (c->which == DATA1N_data || c->which == DATA1N_comment)
150         {
151             char *p = c->u.data.data;
152             int l = c->u.data.len;
153             int first = 1;
154             int lcol = col;
155
156             if (pretty_format && !c->u.data.formatted_text)
157                 indent (b, col);
158             if (c->which == DATA1N_comment)
159                 wrbuf_puts (b, "<!--");
160             switch (c->u.data.what)
161             {
162             case DATA1I_xmltext:
163                 wrbuf_write(b, c->u.data.data, c->u.data.len);
164                 break;
165             case DATA1I_text:
166                 if (!pretty_format || c->u.data.formatted_text)
167                 {
168                     wrbuf_xmlputs_n (b, p, l);
169                 }
170                 else
171                 {
172                     while (l)
173                     {
174                         int wlen;
175                         
176                         while (l && d1_isspace(*p))
177                             p++, l--;
178                         if (!l)
179                             break;
180                         /* break if we cross margin and word is not too long */
181                         if (lcol + (wlen = wordlen(p, l)) > IDSGML_MARGIN &&
182                             wlen < IDSGML_MARGIN)
183                         {
184                             wrbuf_puts (b, "\n");
185                             indent (b, col);
186                             lcol = col;
187                             first = 1;
188                         }
189                         if (!first)
190                         {
191                             wrbuf_putc(b, ' ');
192                             lcol++;
193                         }
194                         while (l && !d1_isspace(*p))
195                         {
196                             wrbuf_putc(b, *p);
197                             p++;
198                             l--;
199                             lcol++;
200                         }
201                         first = 0;
202                     }
203                     wrbuf_puts(b, "\n");
204                 }
205                 break;
206             case DATA1I_num:
207                 wrbuf_xmlputs_n(b, c->u.data.data, c->u.data.len);
208                 if (pretty_format)
209                     wrbuf_puts(b, "\n");
210                 break;
211             case DATA1I_oid:
212                 wrbuf_xmlputs_n(b, c->u.data.data, c->u.data.len);
213                 if (pretty_format)
214                     wrbuf_puts(b, "\n");
215             }
216             if (c->which == DATA1N_comment)
217             {
218                 wrbuf_puts(b, "-->");
219                 if (pretty_format)
220                     wrbuf_puts(b, "\n");
221             }
222         }
223     }
224     return 0;
225 }
226
227 char *data1_nodetoidsgml (data1_handle dh, data1_node *n, int select, int *len)
228 {
229     WRBUF b = data1_get_wrbuf (dh);
230     
231     wrbuf_rewind(b);
232     
233     if (!data1_is_xmlmode (dh))
234     {
235         wrbuf_puts (b, "<");
236         wrbuf_write_tag(b, n->u.root.type, 1);
237         wrbuf_puts (b, ">\n");
238     }
239     if (nodetoidsgml(n, select, b, 0, 0 /* no pretty format */))
240         return 0;
241     if (!data1_is_xmlmode (dh))
242     {
243         wrbuf_puts (b, "</");
244         wrbuf_write_tag(b, n->u.root.type, 0);
245         wrbuf_puts (b, ">\n");
246     }
247     *len = wrbuf_len(b);
248     return wrbuf_buf(b);
249 }
250 /*
251  * Local variables:
252  * c-basic-offset: 4
253  * indent-tabs-mode: nil
254  * End:
255  * vim: shiftwidth=4 tabstop=8 expandtab
256  */
257