[14] | 1 | /* $Id: docdesign.c 785 2007-01-24 22:21:56Z bird $
|
---|
| 2 | *
|
---|
| 3 | * Documentation generator.
|
---|
| 4 | *
|
---|
[785] | 5 | * Copyright (c) 2002 knut st. osmundsen <bird-kBuild-spam@anduin.net>
|
---|
[14] | 6 | *
|
---|
| 7 | *
|
---|
| 8 | * This file is part of kBuild.
|
---|
| 9 | *
|
---|
| 10 | * kBuild is free software; you can redistribute it and/or modify
|
---|
| 11 | * it under the terms of the GNU Lesser General Public License as published
|
---|
| 12 | * by the Free Software Foundation; either version 2 of the License, or
|
---|
| 13 | * (at your option) any later version.
|
---|
| 14 | *
|
---|
| 15 | * kBuild is distributed in the hope that it will be useful,
|
---|
| 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
| 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
| 18 | * GNU Lesser General Public License for more details.
|
---|
| 19 | *
|
---|
| 20 | * You should have received a copy of the GNU Lesser General Public License
|
---|
| 21 | * along with kBuild; if not, write to the Free Software
|
---|
| 22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
---|
| 23 | *
|
---|
| 24 | */
|
---|
| 25 |
|
---|
| 26 |
|
---|
| 27 | /*******************************************************************************
|
---|
| 28 | * Header Files *
|
---|
| 29 | *******************************************************************************/
|
---|
| 30 | #include <string.h>
|
---|
| 31 | #include <stdlib.h>
|
---|
| 32 | #include <stdio.h>
|
---|
| 33 | #include <errno.h>
|
---|
| 34 |
|
---|
| 35 |
|
---|
| 36 | /*******************************************************************************
|
---|
| 37 | * Structures and Typedefs *
|
---|
| 38 | *******************************************************************************/
|
---|
| 39 | typedef struct _Section
|
---|
| 40 | {
|
---|
| 41 | struct _Section *pNext; /* Next Section. */
|
---|
| 42 | int iLevel; /* 0 is @design, 1 is @subsection and so on. */
|
---|
| 43 | char *pszHeader;
|
---|
| 44 | char *pszText; /* Content of the section. */
|
---|
| 45 | int cchText;
|
---|
| 46 | } SECTION, *PSECTION;
|
---|
| 47 |
|
---|
| 48 |
|
---|
| 49 | /*******************************************************************************
|
---|
| 50 | * Global Variables *
|
---|
| 51 | *******************************************************************************/
|
---|
| 52 | PSECTION pSections = NULL;
|
---|
| 53 | PSECTION pSectionsTail = NULL;
|
---|
| 54 |
|
---|
| 55 |
|
---|
| 56 | /**
|
---|
| 57 | * Reads the file parsing out the @design and @sub[..]section
|
---|
| 58 | * parts putting them into the pSections list.
|
---|
| 59 | */
|
---|
| 60 | int ParseFile(const char *pszFilename)
|
---|
| 61 | {
|
---|
| 62 | int rc = 0;
|
---|
| 63 | FILE * phFile;
|
---|
| 64 |
|
---|
| 65 | /*
|
---|
| 66 | * Open the file.
|
---|
| 67 | */
|
---|
| 68 | phFile = fopen(pszFilename, "r");
|
---|
| 69 | if (phFile)
|
---|
| 70 | {
|
---|
| 71 | static char szLine[0x10000];
|
---|
| 72 | enum {enmUnknown, enmSection, enmSectionClosing}
|
---|
| 73 | enmState = enmUnknown;
|
---|
| 74 | PSECTION pCurSection = NULL;
|
---|
| 75 |
|
---|
| 76 | /*
|
---|
| 77 | * Read the file line by line. looking for @design and @sub[..]section.
|
---|
| 78 | */
|
---|
| 79 | while (fgets(szLine, sizeof(szLine), phFile))
|
---|
| 80 | {
|
---|
| 81 | int fNew = 0;
|
---|
| 82 | int iLevel = 0;
|
---|
| 83 | char *psz = szLine;
|
---|
| 84 | char *pszEnd = &szLine[strlen(szLine) - 1];
|
---|
| 85 | /*
|
---|
[15] | 86 | * Strip off any ' ', '\t', '\n', '\r', '\*\/' and '*' from end
|
---|
[14] | 87 | * Strip off any ' ', '\t', '\/\*', '*' and '//' from start.
|
---|
| 88 | * Need to check for closing comment too.
|
---|
| 89 | */
|
---|
| 90 | while ( pszEnd >= psz
|
---|
| 91 | && ( *pszEnd == '*'
|
---|
| 92 | || *pszEnd == ' '
|
---|
| 93 | || *pszEnd == '\t'
|
---|
| 94 | || *pszEnd == '\r'
|
---|
| 95 | || *pszEnd == '\n'
|
---|
| 96 | )
|
---|
| 97 | )
|
---|
| 98 | {
|
---|
| 99 | *pszEnd-- = '\0';
|
---|
| 100 | }
|
---|
| 101 | if (pszEnd > psz && !strcmp(&pszEnd[-1], "*/") && enmState == enmSection)
|
---|
| 102 | enmState = enmSectionClosing;
|
---|
| 103 | while ( pszEnd >= psz
|
---|
| 104 | && ( *pszEnd == '*'
|
---|
| 105 | || *pszEnd == ' '
|
---|
| 106 | || *pszEnd == '\t'
|
---|
| 107 | || *pszEnd == '\n'
|
---|
| 108 | || *pszEnd == '\r'
|
---|
| 109 | || (*pszEnd == '/' && pszEnd > psz && pszEnd[-1] == '*')
|
---|
| 110 | )
|
---|
| 111 | )
|
---|
| 112 | {
|
---|
[15] | 113 | if (*pszEnd == '/')
|
---|
| 114 | *pszEnd-- = '\0';
|
---|
[14] | 115 | *pszEnd-- = '\0';
|
---|
| 116 | }
|
---|
| 117 |
|
---|
[15] | 118 | while ( *psz == '*'
|
---|
| 119 | || *psz == ' '
|
---|
| 120 | || *psz == '\t'
|
---|
| 121 | || (*psz == '/' && (psz[1] == '*' || psz[1] == '/')))
|
---|
| 122 | {
|
---|
| 123 | if (*psz++ == '/')
|
---|
| 124 | psz++;
|
---|
| 125 | }
|
---|
[14] | 126 |
|
---|
[15] | 127 |
|
---|
[14] | 128 | /*
|
---|
| 129 | * Look for tag.
|
---|
| 130 | */
|
---|
| 131 | if (!strncmp(psz, "@design", 7))
|
---|
| 132 | {
|
---|
| 133 | fNew = 1;
|
---|
| 134 | iLevel = 0;
|
---|
| 135 | }
|
---|
| 136 | else if (!strncmp(psz, "@sub", 4))
|
---|
| 137 | {
|
---|
| 138 | char *psz2 = psz + 4;
|
---|
| 139 | fNew = 1;
|
---|
| 140 | iLevel = 1;
|
---|
| 141 | while (!strncmp(psz2, "sub", 3))
|
---|
| 142 | {
|
---|
| 143 | psz2 += 3;
|
---|
| 144 | iLevel++;
|
---|
| 145 | }
|
---|
| 146 | }
|
---|
| 147 |
|
---|
| 148 | /*
|
---|
| 149 | * Action
|
---|
| 150 | */
|
---|
| 151 | if (fNew)
|
---|
| 152 | {
|
---|
| 153 | char *psz2;
|
---|
| 154 | /*
|
---|
| 155 | * New Section.
|
---|
| 156 | * Change state.
|
---|
| 157 | * Allocate new section struct, init it and link it into the list.
|
---|
| 158 | * Get section header.
|
---|
| 159 | */
|
---|
| 160 | if (enmState != enmSectionClosing)
|
---|
| 161 | enmState = enmSection;
|
---|
| 162 |
|
---|
| 163 | pCurSection = malloc(sizeof(*pCurSection));
|
---|
| 164 | memset(pCurSection, 0, sizeof(*pCurSection));
|
---|
| 165 | pCurSection->iLevel = iLevel;
|
---|
| 166 | if (pSectionsTail)
|
---|
| 167 | pSectionsTail = pSectionsTail->pNext = pCurSection;
|
---|
| 168 | else
|
---|
| 169 | pSections = pSectionsTail = pCurSection;
|
---|
| 170 |
|
---|
| 171 | psz2 = strpbrk(psz, " \t");
|
---|
| 172 | if (psz2)
|
---|
| 173 | {
|
---|
| 174 | while (*psz2 == ' ' || *psz == '\t')
|
---|
| 175 | psz2++;
|
---|
| 176 | if (*psz)
|
---|
| 177 | pCurSection->pszHeader = strdup(psz2);
|
---|
| 178 | }
|
---|
| 179 | }
|
---|
| 180 | else if (enmState == enmSection || enmState == enmSectionClosing)
|
---|
| 181 | {
|
---|
| 182 | /*
|
---|
| 183 | * Add text to current section
|
---|
| 184 | */
|
---|
| 185 | int cch = strlen(psz);
|
---|
| 186 | if (!cch && pCurSection->cchText)
|
---|
| 187 | {
|
---|
| 188 | psz = "<p>";
|
---|
| 189 | cch = strlen(psz);
|
---|
| 190 | }
|
---|
| 191 |
|
---|
| 192 | if (cch)
|
---|
| 193 | {
|
---|
| 194 | pCurSection->pszText = realloc(pCurSection->pszText, pCurSection->cchText + cch + 2);
|
---|
| 195 | pCurSection->pszText[pCurSection->cchText++] = '\n';
|
---|
| 196 | strcpy(&pCurSection->pszText[pCurSection->cchText], psz);
|
---|
| 197 | pCurSection->cchText += cch;
|
---|
| 198 | }
|
---|
| 199 | }
|
---|
| 200 |
|
---|
| 201 | /*
|
---|
| 202 | * State transition.
|
---|
| 203 | */
|
---|
| 204 | if (enmState == enmSectionClosing)
|
---|
| 205 | enmState = enmUnknown;
|
---|
| 206 |
|
---|
| 207 | } /* while fgets */
|
---|
| 208 |
|
---|
| 209 | fclose(phFile);
|
---|
| 210 | }
|
---|
| 211 | else
|
---|
| 212 | {
|
---|
| 213 | fprintf(stderr, "error: failed to open %s. errno=%d\n", pszFilename, errno);
|
---|
| 214 | rc = errno;
|
---|
| 215 | }
|
---|
| 216 |
|
---|
| 217 | return rc;
|
---|
| 218 | }
|
---|
| 219 |
|
---|
| 220 |
|
---|
| 221 | /**
|
---|
[15] | 222 | * Checks if psz is point to a tag we pass thru.
|
---|
| 223 | * @returns length of tag if pass thru tag.
|
---|
| 224 | * @returns 0 if not.
|
---|
| 225 | * @param psz Pointer to text string.
|
---|
| 226 | */
|
---|
| 227 | int isTag(const char *psz)
|
---|
| 228 | {
|
---|
| 229 | int i;
|
---|
| 230 | static char * apszTags[] =
|
---|
| 231 | {
|
---|
| 232 | "<b>", "</b>",
|
---|
| 233 | "<i>", "</i>",
|
---|
| 234 | "<ul>", "</ul>",
|
---|
| 235 | "<ol>", "</ol>",
|
---|
| 236 | "<pre>", "</pre>",
|
---|
| 237 | "<h1>", "</h1>",
|
---|
| 238 | "<h2>", "</h2>",
|
---|
| 239 | "<h3>", "</h3>",
|
---|
| 240 | "<h4>", "</h4>",
|
---|
| 241 | "<h5>", "</h5>",
|
---|
| 242 | "<h6>", "</h6>",
|
---|
| 243 | "<li>",
|
---|
| 244 | "<p>",
|
---|
| 245 | "<br>"
|
---|
| 246 | };
|
---|
| 247 |
|
---|
| 248 | if (*psz == '<')
|
---|
| 249 | {
|
---|
| 250 | for (i = 0; i < sizeof(apszTags) / sizeof(apszTags[0]); i++)
|
---|
| 251 | {
|
---|
| 252 | int cch = strlen(apszTags[i]);
|
---|
| 253 | if (!strnicmp(apszTags[i], psz, cch))
|
---|
| 254 | return cch;
|
---|
| 255 | }
|
---|
| 256 | }
|
---|
| 257 |
|
---|
| 258 | return 0;
|
---|
| 259 | }
|
---|
| 260 |
|
---|
| 261 |
|
---|
| 262 | /**
|
---|
| 263 | * HTMLify text and print it.
|
---|
| 264 | * @param pszText Text in question.
|
---|
| 265 | */
|
---|
| 266 | void PutHtmlText(const char *pszText)
|
---|
| 267 | {
|
---|
| 268 | while (*pszText)
|
---|
| 269 | {
|
---|
| 270 | char ch = *pszText;
|
---|
| 271 | char sz[256];
|
---|
| 272 | sz[0] = '\0';
|
---|
| 273 | switch (ch)
|
---|
| 274 | {
|
---|
| 275 | case '<':
|
---|
| 276 | {
|
---|
| 277 | int cch = isTag(pszText);
|
---|
| 278 | if (cch)
|
---|
| 279 | {
|
---|
| 280 | strncat(sz, pszText, cch);
|
---|
| 281 | pszText += cch - 1;
|
---|
| 282 | }
|
---|
| 283 | else
|
---|
| 284 | strcpy(sz, "<");
|
---|
| 285 | break;
|
---|
| 286 | }
|
---|
| 287 |
|
---|
| 288 | case '>':
|
---|
| 289 | strcpy(sz, ">");
|
---|
| 290 | break;
|
---|
| 291 |
|
---|
| 292 | case '&':
|
---|
| 293 | strcpy(sz, "&");
|
---|
| 294 | break;
|
---|
| 295 |
|
---|
| 296 | default:
|
---|
| 297 | sz[0] = ch;
|
---|
| 298 | sz[1] = '\0';
|
---|
| 299 | }
|
---|
| 300 | printf("%s", sz);
|
---|
| 301 | pszText++;
|
---|
| 302 | }
|
---|
| 303 | }
|
---|
| 304 |
|
---|
| 305 |
|
---|
| 306 | /**
|
---|
[14] | 307 | * Keep track and formats section level.
|
---|
| 308 | */
|
---|
| 309 | void SectionNumber(int iLevel, int *paiSections, char *pszSection)
|
---|
| 310 | {
|
---|
| 311 | int i;
|
---|
| 312 |
|
---|
| 313 | paiSections[iLevel]++;
|
---|
| 314 | for (i = iLevel + 1; i < 100; i++)
|
---|
| 315 | paiSections[i] = 0;
|
---|
| 316 |
|
---|
| 317 | sprintf(pszSection, "%d", paiSections[0]);
|
---|
| 318 | if (iLevel == 0)
|
---|
| 319 | strcat(pszSection, ".0");
|
---|
| 320 | else
|
---|
| 321 | {
|
---|
| 322 | for (i = 1; i <= iLevel; i++)
|
---|
| 323 | sprintf(&pszSection[strlen(pszSection)], ".%d", paiSections[i]);
|
---|
| 324 | }
|
---|
| 325 | }
|
---|
| 326 |
|
---|
| 327 |
|
---|
| 328 | /**
|
---|
| 329 | * Outputs the section stuff to stdout as HTML.
|
---|
| 330 | */
|
---|
| 331 | int MakeHTML(void)
|
---|
| 332 | {
|
---|
| 333 | int aiSections[100];
|
---|
| 334 | char szSection[1024];
|
---|
| 335 | PSECTION pCurSection;
|
---|
| 336 |
|
---|
| 337 | #if 0
|
---|
| 338 | /* debug */
|
---|
| 339 | for (pCurSection = pSections; pCurSection; pCurSection = pCurSection->pNext)
|
---|
| 340 | fprintf(stderr, "debug: level=%d cchText=%-4d header=%s \n",
|
---|
| 341 | pCurSection->iLevel, pCurSection->cchText, pCurSection->pszHeader);
|
---|
| 342 | #endif
|
---|
| 343 |
|
---|
| 344 | /*
|
---|
| 345 | * Header
|
---|
| 346 | */
|
---|
| 347 | printf("<!-- Generate by docdesign -->\n"
|
---|
| 348 | "<head>\n"
|
---|
[15] | 349 | "<title>Design Document</title>\n"
|
---|
[14] | 350 | "\n"
|
---|
| 351 | "<body>\n"
|
---|
| 352 | );
|
---|
| 353 |
|
---|
| 354 | /*
|
---|
| 355 | * Content
|
---|
| 356 | */
|
---|
| 357 | printf("<a name=content><h2>Content</h2></a>\n"
|
---|
| 358 | "<ul>\n"
|
---|
| 359 | );
|
---|
| 360 | memset(&aiSections[0], 0, sizeof(aiSections));
|
---|
| 361 | for (pCurSection = pSections; pCurSection; pCurSection = pCurSection->pNext)
|
---|
| 362 | {
|
---|
| 363 | SectionNumber(pCurSection->iLevel, &aiSections[0], szSection);
|
---|
| 364 | printf(" <li><a href=\"#%s\">%s %s</a>\n",
|
---|
| 365 | szSection, szSection, pCurSection->pszHeader);
|
---|
| 366 | }
|
---|
| 367 | printf("</ul>\n"
|
---|
| 368 | "\n");
|
---|
| 369 |
|
---|
| 370 | /*
|
---|
| 371 | * Sections.
|
---|
| 372 | */
|
---|
| 373 | memset(&aiSections[0], 0, sizeof(aiSections));
|
---|
| 374 | for (pCurSection = pSections; pCurSection; pCurSection = pCurSection->pNext)
|
---|
| 375 | {
|
---|
| 376 | int iHNumber = min(pCurSection->iLevel + 1, 5);
|
---|
| 377 | SectionNumber(pCurSection->iLevel, &aiSections[0], szSection);
|
---|
| 378 | printf("<p><br><p>\n"
|
---|
| 379 | "<a href=#content><a name=%s><h%d>%s %s</h%d></a></a>\n"
|
---|
[15] | 380 | "\n",
|
---|
| 381 | szSection, iHNumber, szSection, pCurSection->pszHeader, iHNumber);
|
---|
| 382 | if (pCurSection->pszText)
|
---|
| 383 | PutHtmlText(pCurSection->pszText);
|
---|
[14] | 384 | }
|
---|
| 385 | printf("</ul>\n"
|
---|
| 386 | "\n");
|
---|
| 387 |
|
---|
| 388 |
|
---|
| 389 | /* footer */
|
---|
| 390 | printf("</body>\n"
|
---|
| 391 | "</head>\n");
|
---|
| 392 |
|
---|
| 393 | return -1;
|
---|
| 394 | }
|
---|
| 395 |
|
---|
| 396 |
|
---|
| 397 | int main(int argc, char **argv)
|
---|
| 398 | {
|
---|
| 399 | int rc;
|
---|
| 400 | int argi;
|
---|
| 401 |
|
---|
| 402 | /*
|
---|
| 403 | * Parse arguments.
|
---|
| 404 | */
|
---|
| 405 | for (argi = 1, rc = 0; !rc && argi < argc; argi++)
|
---|
| 406 | {
|
---|
[16] | 407 | if (argv[argi][0] == '@')
|
---|
| 408 | {
|
---|
| 409 | FILE *phFile = fopen(&argv[argi][1], "r");
|
---|
| 410 | if (phFile)
|
---|
| 411 | {
|
---|
| 412 | char szFilename[1024];
|
---|
| 413 | while (!rc && fgets(szFilename, sizeof(szFilename), phFile))
|
---|
| 414 | {
|
---|
| 415 | char *psz = szFilename;
|
---|
| 416 | char *pszEnd = &psz[strlen(psz)] - 1;
|
---|
| 417 | while (*psz == '\t' || *psz == ' ') psz++;
|
---|
| 418 | while (pszEnd >= psz && (*pszEnd == '\t' || *pszEnd == ' ' || *pszEnd == '\n' || *pszEnd == '\r'))
|
---|
| 419 | *pszEnd-- = '\0';
|
---|
| 420 | rc = ParseFile(psz);
|
---|
| 421 | }
|
---|
| 422 | }
|
---|
| 423 | }
|
---|
| 424 | else
|
---|
| 425 | rc = ParseFile(argv[argi]);
|
---|
[14] | 426 | }
|
---|
| 427 |
|
---|
| 428 | if (!rc)
|
---|
| 429 | rc = MakeHTML();
|
---|
| 430 |
|
---|
| 431 | return rc;
|
---|
| 432 | }
|
---|