]> git.ipfire.org Git - thirdparty/cups.git/blob - cgi-bin/makedocset.c
5a72177998079f58a80ac09f7b443c1ac9872c9a
[thirdparty/cups.git] / cgi-bin / makedocset.c
1 /*
2 * Xcode documentation set generator.
3 *
4 * Copyright 2007-2012 by Apple Inc.
5 * Copyright 1997-2007 by Easy Software Products.
6 *
7 * These coded instructions, statements, and computer programs are the
8 * property of Apple Inc. and are protected by Federal copyright
9 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
10 * which should have been included with this file. If this file is
11 * missing or damaged, see the license at "http://www.cups.org/".
12 *
13 * Usage:
14 *
15 * makedocset directory *.tokens
16 */
17
18 /*
19 * Include necessary headers...
20 */
21
22 #include "cgi-private.h"
23 #include <errno.h>
24
25
26 /*
27 * Local structures...
28 */
29
30 typedef struct _cups_html_s /**** Help file ****/
31 {
32 char *path; /* Path to help file */
33 char *title; /* Title of help file */
34 } _cups_html_t;
35
36 typedef struct _cups_section_s /**** Help section ****/
37 {
38 char *name; /* Section name */
39 cups_array_t *files; /* Files in this section */
40 } _cups_section_t;
41
42
43 /*
44 * Local functions...
45 */
46
47 static int compare_html(_cups_html_t *a, _cups_html_t *b);
48 static int compare_sections(_cups_section_t *a, _cups_section_t *b);
49 static int compare_sections_files(_cups_section_t *a, _cups_section_t *b);
50 static void write_index(const char *path, help_index_t *hi);
51 static void write_info(const char *path, const char *revision);
52 static void write_nodes(const char *path, help_index_t *hi);
53
54
55 /*
56 * 'main()' - Test the help index code.
57 */
58
59 int /* O - Exit status */
60 main(int argc, /* I - Number of command-line args */
61 char *argv[]) /* I - Command-line arguments */
62 {
63 int i; /* Looping var */
64 char path[1024], /* Path to documentation */
65 line[1024]; /* Line from file */
66 help_index_t *hi; /* Help index */
67 cups_file_t *tokens, /* Tokens.xml file */
68 *fp; /* Current file */
69
70
71 if (argc < 4)
72 {
73 puts("Usage: makedocset directory revision *.tokens");
74 return (1);
75 }
76
77 /*
78 * Index the help documents...
79 */
80
81 snprintf(path, sizeof(path), "%s/Contents/Resources/Documentation", argv[1]);
82 if ((hi = helpLoadIndex(NULL, path)) == NULL)
83 {
84 fputs("makedocset: Unable to index help files!\n", stderr);
85 return (1);
86 }
87
88 snprintf(path, sizeof(path), "%s/Contents/Resources/Documentation/index.html",
89 argv[1]);
90 write_index(path, hi);
91
92 snprintf(path, sizeof(path), "%s/Contents/Resources/Nodes.xml", argv[1]);
93 write_nodes(path, hi);
94
95 /*
96 * Write the Info.plist file...
97 */
98
99 snprintf(path, sizeof(path), "%s/Contents/Info.plist", argv[1]);
100 write_info(path, argv[2]);
101
102 /*
103 * Merge the Tokens.xml files...
104 */
105
106 snprintf(path, sizeof(path), "%s/Contents/Resources/Tokens.xml", argv[1]);
107 if ((tokens = cupsFileOpen(path, "w")) == NULL)
108 {
109 fprintf(stderr, "makedocset: Unable to create \"%s\": %s\n", path,
110 strerror(errno));
111 return (1);
112 }
113
114 cupsFilePuts(tokens, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
115 cupsFilePuts(tokens, "<Tokens version=\"1.0\">\n");
116
117 for (i = 3; i < argc; i ++)
118 {
119 if ((fp = cupsFileOpen(argv[i], "r")) == NULL)
120 {
121 fprintf(stderr, "makedocset: Unable to open \"%s\": %s\n", argv[i],
122 strerror(errno));
123 return (1);
124 }
125
126 if (!cupsFileGets(fp, line, sizeof(line)) || strncmp(line, "<?xml ", 6) ||
127 !cupsFileGets(fp, line, sizeof(line)) || strncmp(line, "<Tokens ", 8))
128 {
129 fprintf(stderr, "makedocset: Bad Tokens.xml file \"%s\"!\n", argv[i]);
130 return (1);
131 }
132
133 while (cupsFileGets(fp, line, sizeof(line)))
134 {
135 if (strcmp(line, "</Tokens>"))
136 cupsFilePrintf(tokens, "%s\n", line);
137 }
138
139 cupsFileClose(fp);
140 }
141
142 cupsFilePuts(tokens, "</Tokens>\n");
143
144 cupsFileClose(tokens);
145
146 /*
147 * Return with no errors...
148 */
149
150 return (0);
151 }
152
153
154 /*
155 * 'compare_html()' - Compare the titles of two HTML files.
156 */
157
158 static int /* O - Result of comparison */
159 compare_html(_cups_html_t *a, /* I - First file */
160 _cups_html_t *b) /* I - Second file */
161 {
162 return (_cups_strcasecmp(a->title, b->title));
163 }
164
165
166 /*
167 * 'compare_sections()' - Compare the names of two help sections.
168 */
169
170 static int /* O - Result of comparison */
171 compare_sections(_cups_section_t *a, /* I - First section */
172 _cups_section_t *b) /* I - Second section */
173 {
174 return (_cups_strcasecmp(a->name, b->name));
175 }
176
177
178 /*
179 * 'compare_sections_files()' - Compare the number of files and section names.
180 */
181
182 static int /* O - Result of comparison */
183 compare_sections_files(
184 _cups_section_t *a, /* I - First section */
185 _cups_section_t *b) /* I - Second section */
186 {
187 int ret = cupsArrayCount(b->files) - cupsArrayCount(a->files);
188
189 if (ret)
190 return (ret);
191 else
192 return (_cups_strcasecmp(a->name, b->name));
193 }
194
195
196 /*
197 * 'write_index()' - Write an index file for the CUPS help.
198 */
199
200 static void
201 write_index(const char *path, /* I - File to write */
202 help_index_t *hi) /* I - Index of files */
203 {
204 cups_file_t *fp; /* Output file */
205 help_node_t *node; /* Current help node */
206 _cups_section_t *section, /* Current section */
207 key; /* Section search key */
208 _cups_html_t *html; /* Current HTML file */
209 cups_array_t *sections, /* Sections in index */
210 *sections_files,/* Sections sorted by size */
211 *columns[3]; /* Columns in final HTML file */
212 int column, /* Current column */
213 lines[3], /* Number of lines in each column */
214 min_column, /* Smallest column */
215 min_lines; /* Smallest number of lines */
216
217
218 /*
219 * Build an array of sections and their files.
220 */
221
222 sections = cupsArrayNew((cups_array_func_t)compare_sections, NULL);
223
224 for (node = (help_node_t *)cupsArrayFirst(hi->nodes);
225 node;
226 node = (help_node_t *)cupsArrayNext(hi->nodes))
227 {
228 if (node->anchor)
229 continue;
230
231 key.name = node->section ? node->section : "Miscellaneous";
232 if ((section = (_cups_section_t *)cupsArrayFind(sections, &key)) == NULL)
233 {
234 section = (_cups_section_t *)calloc(1, sizeof(_cups_section_t));
235 section->name = key.name;
236 section->files = cupsArrayNew((cups_array_func_t)compare_html, NULL);
237
238 cupsArrayAdd(sections, section);
239 }
240
241 html = (_cups_html_t *)calloc(1, sizeof(_cups_html_t));
242 html->path = node->filename;
243 html->title = node->text;
244
245 cupsArrayAdd(section->files, html);
246 }
247
248 /*
249 * Build a sorted list of sections based on the number of files in each section
250 * and the section name...
251 */
252
253 sections_files = cupsArrayNew((cups_array_func_t)compare_sections_files,
254 NULL);
255 for (section = (_cups_section_t *)cupsArrayFirst(sections);
256 section;
257 section = (_cups_section_t *)cupsArrayNext(sections))
258 cupsArrayAdd(sections_files, section);
259
260 /*
261 * Then build three columns to hold everything, trying to balance the number of
262 * lines in each column...
263 */
264
265 for (column = 0; column < 3; column ++)
266 {
267 columns[column] = cupsArrayNew((cups_array_func_t)compare_sections, NULL);
268 lines[column] = 0;
269 }
270
271 for (section = (_cups_section_t *)cupsArrayFirst(sections_files);
272 section;
273 section = (_cups_section_t *)cupsArrayNext(sections_files))
274 {
275 for (min_column = 0, min_lines = lines[0], column = 1;
276 column < 3;
277 column ++)
278 {
279 if (lines[column] < min_lines)
280 {
281 min_column = column;
282 min_lines = lines[column];
283 }
284 }
285
286 cupsArrayAdd(columns[min_column], section);
287 lines[min_column] += cupsArrayCount(section->files) + 2;
288 }
289
290 /*
291 * Write the HTML file...
292 */
293
294 if ((fp = cupsFileOpen(path, "w")) == NULL)
295 {
296 fprintf(stderr, "makedocset: Unable to create %s: %s\n", path,
297 strerror(errno));
298 exit(1);
299 }
300
301 cupsFilePuts(fp, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 "
302 "Transitional//EN\" "
303 "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
304 "<html>\n"
305 "<head>\n"
306 "<title>CUPS Documentation</title>\n"
307 "<link rel='stylesheet' type='text/css' "
308 "href='cups-printable.css'>\n"
309 "</head>\n"
310 "<body>\n"
311 "<h1 class='title'>CUPS Documentation</h1>\n"
312 "<table width='100%' summary=''>\n"
313 "<tr>\n");
314
315 for (column = 0; column < 3; column ++)
316 {
317 if (column)
318 cupsFilePuts(fp, "<td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>\n");
319
320 cupsFilePuts(fp, "<td valign='top' width='33%'>");
321 for (section = (_cups_section_t *)cupsArrayFirst(columns[column]);
322 section;
323 section = (_cups_section_t *)cupsArrayNext(columns[column]))
324 {
325 cupsFilePrintf(fp, "<h2 class='title'>%s</h2>\n", section->name);
326 for (html = (_cups_html_t *)cupsArrayFirst(section->files);
327 html;
328 html = (_cups_html_t *)cupsArrayNext(section->files))
329 cupsFilePrintf(fp, "<p class='compact'><a href='%s'>%s</a></p>\n",
330 html->path, html->title);
331 }
332 cupsFilePuts(fp, "</td>\n");
333 }
334 cupsFilePuts(fp, "</tr>\n"
335 "</table>\n"
336 "</body>\n"
337 "</html>\n");
338 cupsFileClose(fp);
339 }
340
341
342 /*
343 * 'write_info()' - Write the Info.plist file.
344 */
345
346 static void
347 write_info(const char *path, /* I - File to write */
348 const char *revision) /* I - Subversion revision number */
349 {
350 cups_file_t *fp; /* File */
351
352
353 if ((fp = cupsFileOpen(path, "w")) == NULL)
354 {
355 fprintf(stderr, "makedocset: Unable to create %s: %s\n", path,
356 strerror(errno));
357 exit(1);
358 }
359
360 cupsFilePrintf(fp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
361 "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" "
362 "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
363 "<plist version=\"1.0\">\n"
364 "<dict>\n"
365 "\t<key>CFBundleIdentifier</key>\n"
366 "\t<string>org.cups.docset</string>\n"
367 "\t<key>CFBundleName</key>\n"
368 "\t<string>CUPS Documentation</string>\n"
369 "\t<key>CFBundleVersion</key>\n"
370 "\t<string>%d.%d.%s</string>\n"
371 "\t<key>CFBundleShortVersionString</key>\n"
372 "\t<string>%d.%d.%d</string>\n"
373 "\t<key>DocSetFeedName</key>\n"
374 "\t<string>cups.org</string>\n"
375 "\t<key>DocSetFeedURL</key>\n"
376 "\t<string>http://www.cups.org/org.cups.docset.atom"
377 "</string>\n"
378 "\t<key>DocSetPublisherIdentifier</key>\n"
379 "\t<string>org.cups</string>\n"
380 "\t<key>DocSetPublisherName</key>\n"
381 "\t<string>CUPS</string>\n"
382 "</dict>\n"
383 "</plist>\n",
384 CUPS_VERSION_MAJOR, CUPS_VERSION_MINOR, revision,
385 CUPS_VERSION_MAJOR, CUPS_VERSION_MINOR, CUPS_VERSION_PATCH);
386
387 cupsFileClose(fp);
388 }
389
390
391 /*
392 * 'write_nodes()' - Write the Nodes.xml file.
393 */
394
395 static void
396 write_nodes(const char *path, /* I - File to write */
397 help_index_t *hi) /* I - Index of files */
398 {
399 cups_file_t *fp; /* Output file */
400 int id; /* Current node ID */
401 help_node_t *node; /* Current help node */
402 int subnodes; /* Currently in Subnodes for file? */
403 int needclose; /* Need to close the current node? */
404
405
406 if ((fp = cupsFileOpen(path, "w")) == NULL)
407 {
408 fprintf(stderr, "makedocset: Unable to create %s: %s\n", path,
409 strerror(errno));
410 exit(1);
411 }
412
413 cupsFilePuts(fp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
414 "<DocSetNodes version=\"1.0\">\n"
415 "<TOC>\n"
416 "<Node id=\"0\">\n"
417 "<Name>CUPS Documentation</Name>\n"
418 "<Path>Documentation/index.html</Path>\n"
419 "</Node>\n");
420
421 for (node = (help_node_t *)cupsArrayFirst(hi->nodes), id = 1, subnodes = 0,
422 needclose = 0;
423 node;
424 node = (help_node_t *)cupsArrayNext(hi->nodes), id ++)
425 {
426 if (node->anchor)
427 {
428 if (!subnodes)
429 {
430 cupsFilePuts(fp, "<Subnodes>\n");
431 subnodes = 1;
432 }
433
434 cupsFilePrintf(fp, "<Node id=\"%d\">\n"
435 "<Path>Documentation/%s</Path>\n"
436 "<Anchor>%s</Anchor>\n"
437 "<Name>%s</Name>\n"
438 "</Node>\n", id, node->filename, node->anchor,
439 node->text);
440 }
441 else
442 {
443 if (subnodes)
444 {
445 cupsFilePuts(fp, "</Subnodes>\n");
446 subnodes = 0;
447 }
448
449 if (needclose)
450 cupsFilePuts(fp, "</Node>\n");
451
452 cupsFilePrintf(fp, "<Node id=\"%d\">\n"
453 "<Path>Documentation/%s</Path>\n"
454 "<Name>%s</Name>\n", id, node->filename, node->text);
455 needclose = 1;
456 }
457 }
458
459 if (subnodes)
460 cupsFilePuts(fp, "</Subnodes>\n");
461
462 if (needclose)
463 cupsFilePuts(fp, "</Node>\n");
464
465 cupsFilePuts(fp, "</TOC>\n"
466 "</DocSetNodes>\n");
467
468 cupsFileClose(fp);
469 }