| 1 | /* $Id: docdesign.c 16 2002-10-16 23:21:04Z bird $
|
|---|
| 2 | *
|
|---|
| 3 | * Documentation generator.
|
|---|
| 4 | *
|
|---|
| 5 | * Copyright (c) 2002 knut st. osmundsen <bird@anduin.net>
|
|---|
| 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 | /*
|
|---|
| 86 | * Strip off any ' ', '\t', '\n', '\r', '\*\/' and '*' from end
|
|---|
| 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 | {
|
|---|
| 113 | if (*pszEnd == '/')
|
|---|
| 114 | *pszEnd-- = '\0';
|
|---|
| 115 | *pszEnd-- = '\0';
|
|---|
| 116 | }
|
|---|
| 117 |
|
|---|
| 118 | while ( *psz == '*'
|
|---|
| 119 | || *psz == ' '
|
|---|
| 120 | || *psz == '\t'
|
|---|
| 121 | || (*psz == '/' && (psz[1] == '*' || psz[1] == '/')))
|
|---|
| 122 | {
|
|---|
| 123 | if (*psz++ == '/')
|
|---|
| 124 | psz++;
|
|---|
| 125 | }
|
|---|
| 126 |
|
|---|
| 127 |
|
|---|
| 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 | /**
|
|---|
| 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 | /**
|
|---|
| 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"
|
|---|
| 349 | "<title>Design Document</title>\n"
|
|---|
| 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"
|
|---|
| 380 | "\n",
|
|---|
| 381 | szSection, iHNumber, szSection, pCurSection->pszHeader, iHNumber);
|
|---|
| 382 | if (pCurSection->pszText)
|
|---|
| 383 | PutHtmlText(pCurSection->pszText);
|
|---|
| 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 | {
|
|---|
| 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]);
|
|---|
| 426 | }
|
|---|
| 427 |
|
|---|
| 428 | if (!rc)
|
|---|
| 429 | rc = MakeHTML();
|
|---|
| 430 |
|
|---|
| 431 | return rc;
|
|---|
| 432 | }
|
|---|