/* Copyright 1997 Acorn Computers Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /***************************************************/ /* File : Trace.c */ /* */ /* Purpose: Trace build only debugging functions */ /* - not the sort of thing that hacks */ /* up some text display or whatever; that */ /* is the domain of TML. These functions */ /* are for principally file-based output */ /* of things like token list dumps. */ /* */ /* Author : A.D.Hodgkinson */ /* */ /* History: 02-Jul-97: Created. */ /***************************************************/ #ifdef TRACE #include <stdlib.h> #include <stdio.h> #include <string.h> #include "swis.h" #include "HTMLLib.h" /* HTML library API, Which will include html2_ext.h, tags.h and struct.h */ #include "wimp.h" #include "wimplib.h" #include "event.h" #include "svcprint.h" #include "Global.h" #include "MiscDefs.h" #include "Utils.h" #include "Trace.h" /* Local definitions */ #define Trace_DumpTokens_Filename_L "<Browse$Dir>.Tokens-L" #define Trace_DumpTokens_Filename_S "<Browse$Dir>.Tokens-S" #define TrOut(f, d, p) { \ int temp_debug; \ \ for (temp_debug = 0; temp_debug < (d); temp_debug ++) \ { \ if (fprintf((f), "\t") < 0) return 1; \ } \ \ if (fprintf p < 0) return 1; \ } \ /* Static function prototypes */ static int trace_dump_tokens_by_line_r (browser_data * b, reformat_cell * cell, FILE * file, int depth); static int trace_dump_tokens_by_stream_r (browser_data * b, HStream * streambase, FILE * file, int depth); static int trace_dump_hstream (FILE * file, HStream * token, int depth); static int trace_dump_tstream (FILE * file, table_stream * table, int depth); static int trace_dump_row (FILE * file, table_row * row, int depth); static int trace_dump_head (FILE * file, table_headdata * head, int depth); static const char * trace_tag_name (tag_no tagno); static char * trace_add_description (char * list, const char * description); static char * trace_style_bits (HStream * t); /*************************************************/ /* trace_dump_tokens_by_line() */ /* */ /* Outputs a diagnostic description of the */ /* token list of a browser. The browser is */ /* obtained by getting the client handle of the */ /* ancestor of whatever object raised the */ /* ETraceTokenDumpByLine event (see TBEvents.h). */ /* */ /* This function follows line arrays to get the */ /* tokens (so not every token output by the */ /* library will be included, but those that the */ /* browser is actually using will be). */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ int trace_dump_tokens_by_line(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { FILE * tfile; browser_data * b; _kernel_oserror * e; /* Try to open out the file */ tfile = fopen(Trace_DumpTokens_Filename_L, "wb"); if (!tfile) { erb.errnum = Utils_Error_Custom_Message; StrNCpy0(erb.errmess, "trace_dump_tokens: Error trying to open output file"); show_error_ret(&erb); return 0; } /* Try to get the browser_data structure */ e = toolbox_get_client_handle(0, idb->ancestor_id, (void *) &b); if (e) { show_error_ret(e); return 0; } if (!is_known_browser(b)) { erb.errnum = Utils_Error_Custom_Message; sprintf(erb.errmess, "trace_dump_tokens: Can't determine what token list to use ('%08x' is not a known browser)", (int) b); show_error_ret(&erb); return 0; } /* OK, can proceed */ if (fprintf(tfile, "Token dump list for browser %08x\n\n\n\n", (int) b) < 0) goto tdtbl_fo_err; if (trace_dump_tokens_by_line_r(b, b->cell, tfile, 0)) goto tdtbl_fo_err; /* Finished */ fclose(tfile); _swix(OS_CLI, _IN(0), "SetType " Trace_DumpTokens_Filename_L " fff"); return 1; /* Now some special case code */ tdtbl_fo_err: /* If there's an error whilst writing to the file */ fclose(tfile); erb.errnum = Utils_Error_Custom_Message; StrNCpy0(erb.errmess, "trace_dump_tokens: Error whilst writing token dump file"); show_error_ret(e); return 0; } /*************************************************/ /* trace_dump_tokens_by_line_r() */ /* */ /* Recursive back-end to */ /* trace_dump_tokens_by_line. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the overall stream; */ /* */ /* Pointer to a reformat_cell struct */ /* holding the lines to examine; */ /* */ /* Pointer to a FILE structure for */ /* outputting the information; */ /* */ /* Nesting depth. */ /* */ /* Returns: 1 if there was an error, else 0. */ /*************************************************/ static int trace_dump_tokens_by_line_r(browser_data * b, reformat_cell * cell, FILE * file, int depth) { int line, chunk, chunkmax; unsigned int valid; HStream * token = NULL; HStream * last = NULL; _kernel_oserror * e; TrOut(file, depth, (file, "Browser %08x, cell %08x:\n\n\n\n", (int) b, (int) cell)); /* If no lines, is there apparently line or chunk data allocated? */ if (!cell->nlines) { TrOut(file, depth, (file, "This cell has no lines.\n\n")); if (cell->ldata || cell->cdata) { TrOut(file, depth, (file, "The cell appears corrupt; nlines = 0, but ldata = %08x and cdata = %08x.\n\n", (int) cell->ldata, (int) cell->cdata)); } return 0; } /* If there is line data, but nlines is non-zero, the cell is broken */ if (!cell->ldata) { TrOut(file, depth, (file, "This cell appears corrupted - nlines = %d, but ldata = NULL.\n\n", cell->nlines)); return 0; } /* If we get here, there are lines and line data. If there's no chunk data, */ /* the cell isn't necessarily broken but we should flag a warning - various */ /* areas of the browser won't be able to handle this. */ if (!cell->cdata) { TrOut(file, depth, (file, "Warning: nlines = %d, ldata = %08x, but cdata = NULL.\n\n", cell->nlines, (int) cell->ldata)); } for (line = 0; line < cell->nlines; line ++) { chunk = cell->ldata[line].chunks; chunkmax = cell->ldata[line].n + chunk; for (/* 'chunk' is already initialised */ ; chunk < chunkmax; chunk ++) { token = cell->cdata[chunk].t; if (token != last) { last = token; /* Output info on the token as an HStream */ TrOut(file, depth, (file, "Line %d chunk %d, new token %08x:\n\n", line, chunk, (int) token)); /* Deal with tables */ if (token->tagno == TAG_TABLE) { TrOut(file, depth, (file, "This token represents a table. As a table_stream struct:\n\n")); if (trace_dump_tstream(file, (table_stream *) token, depth)) return 1; { table_stream * table = (table_stream *) token; table_row * row = table->List; table_headdata * head = NULL; reformat_cell * c = NULL; reformat_cell * cellarray = table->cells; int cellmax = table->ColSpan * table->RowSpan; int cellindex; if (cellarray) { { int i; TrOut(file, depth, (file, "Cell array contents:\n\n")); for (i = 0; i < cellmax; i ++) { TrOut(file, depth, (file, "[%d]\t%08x\n", (int) i, (int) &cellarray[i])); } TrOut(file, depth, (file, "\n")); } while (row) { head = row->List; while ( head && head->RowOffs < table->RowSpan && head->ColOffs < table->ColSpan ) { switch (head->Tag) { case TagTableData: case TagTableHead: { /* Find the reformat_cell structure for this table cell */ cellindex = head->RowOffs * table->ColSpan + head->ColOffs; if (cellindex < cellmax) { c = &cellarray[cellindex]; TrOut(file, 0, (file, "\n\n")); if (c) { if (trace_dump_tokens_by_line_r(b, c, file, depth + 1)) return 1; } else { TrOut(file, depth, (file, "Warning: Entry %d in cellarray is NULL\n", cellindex)); } } } } if (head->Next) { /* Check the 'next' pointer is valid */ e = _swix(OS_ValidateAddress, _INR(0,1) | _OUT(_FLAGS), head->Next, head->Next + 1, &valid); if (e || (valid & _C)) { fprintf(file, "Warning: This table_headdata has an invalid 'Next' pointer - aborting list here\n"); return 1; } } /* Get next item */ head = head->Next; } if (row->Next) { /* Check the 'next' pointer is valid */ e = _swix(OS_ValidateAddress, _INR(0,1) | _OUT(_FLAGS), row->Next, row->Next + 1, &valid); if (e || (valid & _C)) { fprintf(file, "Warning: This table_row has an invalid 'Next' pointer - aborting list here\n"); return 1; } } /* Get next item */ row = row->Next; } } else { TrOut(file, depth, (file, "Warning: No cellarray - can't scan the lines inside the table.\n")); } } } else { /* Normal tokens */ if (trace_dump_hstream(file, token, depth)) return 1; } } else { TrOut(file, depth, (file, "Line %d chunk %d, same token (%08x) as last chunk.\n\n", line, chunk, (int) token)); } } } return 0; } /*************************************************/ /* trace_dump_tokens_by_stream() */ /* */ /* Outputs a diagnostic description of the */ /* token list of a browser. The browser is */ /* obtained by getting the client handle of the */ /* ancestor of whatever object raised the */ /* ETraceTokenDumpByStream event (see */ /* TBEvents.h). */ /* */ /* This function follows the raw stream to get */ /* each token, so everything output by the */ /* library should be included, even if the */ /* browser isn't actually using it. */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ int trace_dump_tokens_by_stream(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { FILE * tfile; browser_data * b; _kernel_oserror * e; /* Try to open out the file */ tfile = fopen(Trace_DumpTokens_Filename_S, "wb"); if (!tfile) { erb.errnum = Utils_Error_Custom_Message; StrNCpy0(erb.errmess, "trace_dump_tokens: Error trying to open output file"); show_error_ret(&erb); return 0; } /* Try to get the browser_data structure */ e = toolbox_get_client_handle(0, idb->ancestor_id, (void *) &b); if (e) { show_error_ret(e); return 0; } if (!is_known_browser(b)) { erb.errnum = Utils_Error_Custom_Message; sprintf(erb.errmess, "trace_dump_tokens: Can't determine what token list to use ('%08x' is not a known browser)", (int) b); show_error_ret(&erb); return 0; } /* OK, can proceed */ if (fprintf(tfile, "Token dump list for browser %08x\n\n\n\n", (int) b) < 0) goto tdtbs_fo_err; if (trace_dump_tokens_by_stream_r(b, b->stream, tfile, 0)) goto tdtbs_fo_err; /* Finished */ fclose(tfile); _swix(OS_CLI, _IN(0), "SetType " Trace_DumpTokens_Filename_S " fff"); return 1; /* Now some special case code */ tdtbs_fo_err: /* If there's an error whilst writing to the file */ fclose(tfile); erb.errnum = Utils_Error_Custom_Message; StrNCpy0(erb.errmess, "trace_dump_tokens: Error whilst writing token dump file"); show_error_ret(e); return 0; } /*************************************************/ /* trace_dump_tokens_by_stream_r() */ /* */ /* Recursive back-end to */ /* trace_dump_tokens_by_stream. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the overall stream; */ /* */ /* Pointer to the first HStream */ /* struct to examine; */ /* */ /* Pointer to a FILE structure for */ /* outputting the information. */ /* */ /* Nesting depth. */ /* */ /* Returns: 1 if there was an error, else 0. */ /*************************************************/ static int trace_dump_tokens_by_stream_r(browser_data * b, HStream * streambase, FILE * file, int depth) { HStream * token = streambase; unsigned int valid; _kernel_oserror * e; TrOut(file, depth, (file, "Browser %08x, stream base %08x:\n\n\n\n", (int) b, (int) streambase)); while (token) { TrOut(file, depth, (file, "Token %08x:\n\n", (int) token)); /* Deal with tables */ if (token->tagno == TAG_TABLE) { TrOut(file, depth, (file, "This token represents a table. As a table_stream struct:\n\n")); if (trace_dump_tstream(file, (table_stream *) token, depth)) return 1; /* Recursive table scan */ { table_stream * table = (table_stream *) token; table_row * row = table->List; table_headdata * head = NULL; while (row) { TrOut(file, depth, (file, "Table %08x: Row %08x\n\n", (int) table, (int) row)); if (trace_dump_row(file, row, depth + 1)) return 1; head = row->List; while (head) { HStream * base = (HStream *) head->List; TrOut(file, depth, (file, "Table %08x, row %08x: Head %08x\n\n", (int) table, (int) row, (int) head)); if (trace_dump_head(file, head, depth + 1)) return 1; TrOut(file, 0, (file, "\n\n")); if (trace_dump_tokens_by_stream_r(b, base, file, depth + 1)) return 1; if (head->Next) { /* Check the 'next' pointer is valid */ e = _swix(OS_ValidateAddress, _INR(0,1) | _OUT(_FLAGS), head->Next, head->Next + 1, &valid); if (e || (valid & _C)) { fprintf(file, "Warning: This table_headdata has an invalid 'Next' pointer - aborting list here\n"); return 1; } } /* Get next item */ head = head->Next; } if (row->Next) { /* Check the 'next' pointer is valid */ e = _swix(OS_ValidateAddress, _INR(0,1) | _OUT(_FLAGS), row->Next, row->Next + 1, &valid); if (e || (valid & _C)) { fprintf(file, "Warning: This table_row has an invalid 'Next' pointer - aborting list here\n"); return 1; } } /* Get next item */ row = row->Next; } } } else if (token->tagno == TAG_OBJECT) { /* Deal with objects */ TrOut(file, depth, (file, "This token represents an object.\n\n")); if (trace_dump_hstream(file, token, depth)) return 1; TrOut(file, depth, (file, "Object %08x:\n\n", (int) token)); if (trace_dump_tokens_by_stream_r(b, HtmlOBJECTstream(token), file, depth + 1)) return 1; } else { /* Normal tokens */ if (trace_dump_hstream(file, token, depth)) return 1; } if (token->next) { /* Check the 'next' pointer is valid */ e = _swix(OS_ValidateAddress, _INR(0,1) | _OUT(_FLAGS), token->next, token->next + 1, &valid); if (e || (valid & _C)) { fprintf(file, "Warning: This token has an invalid 'next' pointer - aborting list here\n"); return 1; } } /* Follow the list */ token = token->next; } return 0; } /*************************************************/ /* trace_dump_hstream() */ /* */ /* Output the contents of an HStream in a */ /* readable form to the given file. */ /* */ /* Parameters: Pointer to a FILE structure for */ /* the file to write to; */ /* */ /* Pointer to the HStream struct; */ /* */ /* Nesting depth. */ /* */ /* Returns: 0 for success, 1 for failure (an */ /* error, usually when writing to */ /* the file). */ /*************************************************/ static int trace_dump_hstream(FILE * file, HStream * token, int depth) { unsigned int mask; char * style_info; /* Display the known values in the flags word */ TrOut(file, depth, (file, "flags\t\t0x%x\n", (int) token->flags)); if (token->flags & HFlags_DealtWithToken) { TrOut(file, depth, (file, "\t\tHFlags_DealtWithToken\n")); } if (token->flags & HFlags_LinkVisited) { TrOut(file, depth, (file, "\t\tHFlags_LinkVisited\n")); } if (token->flags & HFlags_IgnoreObject) { TrOut(file, depth, (file, "\t\tHFlags_IgnoreObject\n")); } mask = HFlags_DealtWithToken | HFlags_LinkVisited | HFlags_IgnoreObject; if (token->flags & ~mask) { TrOut(file, depth, (file, "\t\tWarning: undefined flags bits set\n")); } TrOut(file, depth, (file, "parent\t\t0x%x\n", (int) token->parent)); /* Deal with known 'type' field values */ TrOut(file, depth, (file, "type\t\t%08x\n", (int) token->type)); if (ISHEAD(token)) { TrOut(file, depth, (file, "\t\tISHEAD\n")); } if (ISBODY(token)) { TrOut(file, depth, (file, "\t\tISBODY\n")); } if (ISNULL(token)) { TrOut(file, depth, (file, "\t\tISNULL\n")); } if (ISFRAMESET(token)) { TrOut(file, depth, (file, "\t\tISFRAMESET\n")); } /* General info */ style_info = trace_style_bits(token); if (style_info) { TrOut(file, depth, (file, "style\t\t%08x: %s\n", (int) token->style, style_info)); free(style_info); } else { if (!token->style) { TrOut(file, depth, (file, "style\t\t%08x: (NULL)\n", (int) token->style)); } else { TrOut(file, depth, (file, "style\t\t%08x: (Can't generate description)\n", (int) token->style)); } } // TrOut(file, depth, (file, "style2\t\t%08x\n", (int) token->style2)); TrOut(file, depth, (file, "indent\t\t0x%x\n", (int) token->indent)); TrOut(file, depth, (file, "anchor\t\t%08x: '%s'\n", (int) token->anchor, token->anchor ? token->anchor : "(NULL)")); TrOut(file, depth, (file, "text\t\t%08x: '%s'\n", (int) token->text, token->text ? token->text : "(NULL)")); TrOut(file, depth, (file, "tag\t\t%08x\n", (int) token->tag)); TrOut(file, depth, (file, "tagno\t\t%d\t: <%s>\n", token->tagno, trace_tag_name(token->tagno))); TrOut(file, depth, (file, "src\t\t%08x: '%s'\n", (int) token->src, token->src ? token->src : "(NULL)")); TrOut(file, depth, (file, "next\t\t%08x\n", (int) token->next)); TrOut(file, depth, (file, "prev\t\t%08x\n", (int) token->prev)); TrOut(file, depth, (file, "enctype\t\t%08x: '%s'\n",(int) token->enctype, token->enctype ? token->enctype : "(NULL)")); TrOut(file, depth, (file, "name\t\t%08x: '%s'\n", (int) token->name, token->name ? token->name : "(NULL)")); TrOut(file, depth, (file, "value\t\t%08x: '%s'\n", (int) token->value, token->value ? token->value : "(NULL)")); TrOut(file, depth, (file, "target\t\t%08x: '%s'\n", (int) token->target, token->target ? token->target : "(NULL)")); TrOut(file, depth, (file, "coords\t\t0x%08x: ", (int) token->coords)); /* Deal with coordinates - i.e. image map areas */ if (token->coords) { areashape type; int n; type = (areashape) token->coords[0]; n = token->coords[1]; switch (type) { case areashape_RECT: { if (fprintf(file, "RECT (%d coords): ", n) < 0) return 1; if (n != 4) { if (fprintf(file, "Illegal\n") < 0) return 1; } else { if (fprintf(file, "lx %d, ty %d, rx %d, by %d\n", token->coords[2], token->coords[3], token->coords[4], token->coords[5]) < 0) return 1; } } break; case areashape_CIRCLE: { if (fprintf(file, "CIRCLE (%d coords): ", n) < 0) return 1; if (n != 3) { if (fprintf(file, "Illegal\n") < 0) return 1; } else { if (fprintf(file, "x %d, y %d, r %d\n", token->coords[2], token->coords[3], token->coords[4]) < 0) return 1; } } break; case areashape_POLY: { if (fprintf(file, "POLY (%d coords)", n) < 0) return 1; if (n < 6 || n % 2) { if (fprintf(file, ": Illegal\n") < 0) return 1; } else { int vc; if (fprintf(file, "\n\n") < 0) return 1; TrOut(file, depth, (file, "\t\t\t x y\n")); for (vc = 0; vc < n; vc += 2) { TrOut(file, depth, (file, "\t\t\t%8d%8d\n", token->coords[vc + 2], token->coords[vc + 3])); } } } break; case areashape_DEFAULT: { if (fprintf(file, "DEFAULT\n") < 0) return 1; } break; } } else if (fprintf(file, "(NULL)\n") < 0) return 1; /* Continue on with other fields */ TrOut(file, depth, (file, "size\t\t0x%x\n", (int) token->size)); TrOut(file, depth, (file, "maxlen\t\t0x%x\n", (int) token->maxlen)); TrOut(file, depth, (file, "rows\t\t0x%x\n", (int) token->rows)); TrOut(file, depth, (file, "cols\t\t0x%x\n", (int) token->cols)); TrOut(file, depth, (file, "colour\t\t0x%06xxx\n", (int) token->colour)); TrOut(file, depth, (file, "fontsize\t%d\n", (int) token->fontsize)); switch (token->tagno) { case TAG_PARAM: { const char *valuetypename[3] = { "DATA", "REF", "OBJECT" }; TrOut(file, depth, (file, "NAME\t\t'%s'\n", HtmlPARAMname(token) ? HtmlPARAMname(token) : "(NULL)")); TrOut(file, depth, (file, "VALUE\t\t'%s'\n", HtmlPARAMvalue(token) ? HtmlPARAMvalue(token) : "(NULL)")); TrOut(file, depth, (file, "VALUETYPE\t%s\n", valuetypename[HtmlPARAMvaluetype(token)])); TrOut(file, depth, (file, "TYPE\t\t'%s'\n", HtmlPARAMtype(token) ? HtmlPARAMtype(token) : "(NULL)")); } break; case TAG_FORM: { TrOut(file, depth, (file, "METHOD\t\t%d\n", HtmlFORMmethod(token))); TrOut(file, depth, (file, "ACTION\t\t%s\n", HtmlFORMaction(token) ? HtmlFORMaction(token) : "(NULL)")); TrOut(file, depth, (file, "TARGET\t\t%s\n", HtmlFORMtarget(token) ? HtmlFORMtarget(token) : "(NULL)")); } break; case TAG_INPUT: { TrOut(file, depth, (file, "NAME\t\t%s\n", HtmlINPUTname(token) ? HtmlINPUTname(token) : "(NULL)")); TrOut(file, depth, (file, "VALUE\t\t%s\n", HtmlINPUTvalue(token) ? HtmlINPUTvalue(token) : "(NULL)")); TrOut(file, depth, (file, "SRC\t\t%s\n", HtmlINPUTsrc(token) ? HtmlINPUTsrc(token) : "(NULL)")); TrOut(file, depth, (file, "ALT\t\t%s\n", HtmlINPUTalt(token) ? HtmlINPUTalt(token) : "(NULL)")); } } TrOut(file, depth, (file, "\n")); return 0; } /*************************************************/ /* trace_dump_tstream() */ /* */ /* Output the contents of a table_stream in a */ /* readable form to the given file. */ /* */ /* Parameters: Pointer to a FILE structure for */ /* the file to write to; */ /* */ /* Pointer to the table_stream */ /* structure; */ /* */ /* Nesting depth. */ /* */ /* Returns: 0 for success, 1 for failure (an */ /* error, usually when writing to */ /* the file). */ /*************************************************/ static int trace_dump_tstream(FILE * file, table_stream * table, int depth) { unsigned int mask; char * style_info; /* Display the flags */ TrOut(file, depth, (file, "flags\t\t0x%x\n", (int) table->flags)); if (table->flags & HFlags_DealtWithToken) { TrOut(file, depth, (file, "\t\tHFlags_DealtWithToken\n")); } if (table->flags & HFlags_LinkVisited) { TrOut(file, depth, (file, "\t\tHFlags_LinkVisited\n")); } mask = HFlags_DealtWithToken | HFlags_LinkVisited; if (table->flags & ~mask) { TrOut(file, depth, (file, "\t\tWarning: undefined flags bits set\n")); } TrOut(file, depth, (file, "parent\t\t0x%x\n", (int) table->parent)); /* Deal with known cases of the 'type' field */ TrOut(file, depth, (file, "type\t\t%08x\n", (int) table->type)); if (ISHEAD(table)) { TrOut(file, depth, (file, "\t\tISHEAD\n")); } if (ISBODY(table)) { TrOut(file, depth, (file, "\t\tISBODY\n")); } if (ISNULL(table)) { TrOut(file, depth, (file, "\t\tISNULL\n")); } if (ISFRAMESET(table)) { TrOut(file, depth, (file, "\t\tISFRAMESET\n")); } style_info = trace_style_bits((HStream *) table); if (style_info) { TrOut(file, depth, (file, "style\t\t%08x: %s\n", (int) table->style, style_info)); free(style_info); } else { if (!table->style) { TrOut(file, depth, (file, "style\t\t%08x: (NULL)\n", (int) table->style)); } else { TrOut(file, depth, (file, "style\t\t%08x: (Can't generate description)\n", (int) table->style)); } } // TrOut(file, depth, (file, "style2\t\t%08x\n", (int) table->style2)); TrOut(file, depth, (file, "Tag\t\t%08x\n", (int) table->Tag)); TrOut(file, depth, (file, "ColSpan\t\t%d\n", (int) table->ColSpan)); TrOut(file, depth, (file, "RowSpan\t\t%d\n", (int) table->RowSpan)); /* Display the arrays of row and column offsets, if possible */ TrOut(file, depth, (file, "ColOffs\t\t%08x\n", (int) table->ColOffs)); if (table->ColOffs) { int col, limit; /* Try to use the table_stream's record of number of rows/cols - only */ /* filled in if the browser has parsed the table. If this is zero, */ /* use 16 (more or less arbitrary); otherwise, output as many as */ /* indicated plus 4 to check that the array wasn't overshot (should */ /* commonly see silly values in those extra 4). Don't do more than 64 */ /* though, or we'll be here all day... (Potentially, a corrupted or */ /* uninitialised ColSpan/RowSpan could indicate a *very* large number */ /* of rows or columns!). */ limit = table->ColSpan; if (limit == 0) limit = 16; else limit += 4; if (limit > 64) limit = 64; for (col = 0; col < limit; col ++) { TrOut(file, depth, (file, "\t\t[%d]\t%08x (%08x)\n", col, (int) table->ColOffs[col], (int) table->ColOffs[col]/400)); if (table->ColSpan && col == table->ColSpan - 1) { TrOut(file, depth, (file, "\t\t(This is probably the last entry)\n")); } } } TrOut(file, depth, (file, "RowOffs\t\t%08x\n", (int) table->RowOffs)); /* Same as above, for rows */ if (table->RowOffs) { int row, limit; limit = table->RowSpan; if (limit == 0) limit = 16; else limit += 4; if (limit > 64) limit = 64; for (row = 0; row < limit; row ++) { TrOut(file, depth, (file, "\t\t[%d]\t%08x (%08x)\n", row, (int) table->RowOffs[row], (int) table->RowOffs[row]/400)); if (table->RowSpan && row == table->RowSpan - 1) { TrOut(file, depth, (file, "\t\t(This is probably the last entry)\n")); } } } /* General info */ TrOut(file, depth, (file, "Next\t\t%08x\n", (int) table->Next)); TrOut(file, depth, (file, "Prev\t\t%08x\n", (int) table->Prev)); TrOut(file, depth, (file, "List\t\t%08x\n", (int) table->List)); TrOut(file, depth, (file, "bgcol\t\t%08x\n", (int) table->bgcol)); TrOut(file, depth, (file, "background\t%08x: '%s'\n", (int) table->background, table->background ? table->background : "(NULL)")); TrOut(file, depth, (file, "cols\t\t%d\n", (int) table->cols)); TrOut(file, depth, (file, "width\t\t%d\n", (int) table->width)); TrOut(file, depth, (file, "height\t\t%d\n", (int) table->height)); TrOut(file, depth, (file, "border\t\t%d\n", (int) table->border)); TrOut(file, depth, (file, "cellspacing\t%d\n", (int) table->cellspacing)); TrOut(file, depth, (file, "cellpadding\t%d\n", (int) table->cellpadding)); TrOut(file, depth, (file, "stackedstyle\t%08x\n", (int) table->stackedstyle)); TrOut(file, depth, (file, "cells\t\t%08x\n", (int) table->cells)); TrOut(file, depth, (file, "Align\t\t0x%x\n", (int) table->Align)); /* Deal with specific bitfield flags */ if (table->awaiting_tr) { TrOut(file, depth, (file, "awaiting_tr\tyes\n")); } else { TrOut(file, depth, (file, "awaiting_tr\tno\n")); } if (table->finished) { TrOut(file, depth, (file, "finished\tyes\n\n")); } else { TrOut(file, depth, (file, "finished\tno\n\n")); } return 0; } /*************************************************/ /* trace_dump_row() */ /* */ /* Output the contents of a table_row in a */ /* readable form to the given file. */ /* */ /* Parameters: Pointer to a FILE structure for */ /* the file to write to; */ /* */ /* Pointer to the table_row struct; */ /* */ /* Nesting depth. */ /* */ /* Returns: 0 for success, 1 for failure (an */ /* error, usually when writing to */ /* the file). */ /*************************************************/ static int trace_dump_row(FILE * file, table_row * row, int depth) { /* General info */ TrOut(file, depth, (file, "Next\t\t%08x\n", (int) row->Next)); TrOut(file, depth, (file, "Prev\t\t%08x\n", (int) row->Prev)); TrOut(file, depth, (file, "parent\t\t%08x\n", (int) row->parent)); TrOut(file, depth, (file, "List\t\t%08x\n", (int) row->List)); /* 8 bit values */ TrOut(file, depth, (file, "align\t\t%d\n", (int) row->align)); TrOut(file, depth, (file, "valign\t\t%d\n", (int) row->valign)); /* Background colour (inherited by cells on the row) */ TrOut(file, depth, (file, "bgcol\t\t%08x\n\n",(int) row->bgcol)); return 0; } /*************************************************/ /* trace_dump_head() */ /* */ /* Output the contents of a table_headdata in a */ /* readable form to the given file. */ /* */ /* Parameters: Pointer to a FILE structure for */ /* the file to write to; */ /* */ /* Pointer to the table_headdata */ /* structure; */ /* */ /* Nesting depth. */ /* */ /* Returns: 0 for success, 1 for failure (an */ /* error, usually when writing to */ /* the file). */ /*************************************************/ static int trace_dump_head(FILE * file, table_headdata * head, int depth) { TrOut(file, depth, (file, "ColSpan\t\t%d\n", (int) head->ColSpan)); TrOut(file, depth, (file, "RowSpan\t\t%d\n", (int) head->RowSpan)); TrOut(file, depth, (file, "ColOffs\t\t%d\n", (int) head->ColOffs)); TrOut(file, depth, (file, "RowOffs\t\t%d\n", (int) head->RowOffs)); TrOut(file, depth, (file, "Tag\t\t0x%x\n", (int) head->Tag)); switch (head->Tag) { case TagTableHead: { TrOut(file, depth, (file, "\t\tTagTableHead\n")); } break; case TagTableData: { TrOut(file, depth, (file, "\t\tTagTableData\n")); } break; default: { TrOut(file, depth, (file, "\t\tWarning: Unrecognised Tag type\n")); } break; } /* General info */ TrOut(file, depth, (file, "parent\t\t%08x\n", (int) head->parent)); TrOut(file, depth, (file, "Next\t\t%08x\n", (int) head->Next)); TrOut(file, depth, (file, "Prev\t\t%08x\n", (int) head->Prev)); TrOut(file, depth, (file, "List\t\t%08x\n", (int) head->List)); /* 8 bit values */ TrOut(file, depth, (file, "Align\t\t%d\n", (int) head->Align)); TrOut(file, depth, (file, "VAlign\t\t%d\n", (int) head->VAlign)); /* Background image */ TrOut(file, depth, (file, "background\t%08x: '%s'\n", (int) head->background, head->background ? head->background : "(NULL)")); /* More general info */ TrOut(file, depth, (file, "width\t\t%d (%d)\n", (int) head->width, (int) head->width / 400)); TrOut(file, depth, (file, "height\t\t%d (%d)\n",(int) head->height, (int) head->height / 400)); /* Background colour */ TrOut(file, depth, (file, "bgcol\t\t%08x\n\n", (int) head->bgcol)); return 0; } /*************************************************/ /* trace_dump_buffer() */ /* */ /* Outputs a given buffer's contents over TML, */ /* with unprintable chars shown in green (well, */ /* this depends on the buffer type - see below) */ /* as '[xx]', where xx is the character's ASCII */ /* code in hex. */ /* */ /* For clarity at the output terminal, it is */ /* possible to specify the type of buffer - a */ /* buffer for data transmission, reception, or */ /* a miscellaneous buffer. Control code colours */ /* will change for this; green for transmission, */ /* red for reception, cyan for others. */ /* */ /* Parameters: Pointer to the buffer; */ /* */ /* Size of the buffer; */ /* */ /* 1 for a transmit buffer, 2 for a */ /* receive buffer, else 'other'. */ /*************************************************/ void trace_dump_buffer(void * buffer, int buffer_size, int type) { int i; char c; /* Is the buffer empty (well, zero bytes long or less...)? */ if (buffer_size < 1) { if (type == 1) Printf("\nTransmission buffer %p empty\n\n", buffer); else if (type == 2) Printf("\nReception buffer %p empty\n\n", buffer); else Printf("\nGeneral buffer %p empty\n\n", buffer); return; } /* If not, dump the contents - first, a header message */ if (type == 1) Printf("\nTransmission buffer %p contents:\n\n", buffer); else if (type == 2) Printf("\nReception buffer %p contents:\n\n", buffer); else Printf("\nGeneral buffer %p contents:\n\n", buffer); /* Now, the body of the buffer */ for (i = 0; i < buffer_size; i++) { c = *((char *) buffer + i); if (type == 1) { if (c < 32 || c == 127) Printf("\0212[%02x]\0217", (int) c); else Printf("%c", c); } else if (type == 2) { if (c < 32 || c == 127) Printf("\0211[%02x]\0217", (int) c); else Printf("%c", c); } else { if (c < 32 || c == 127) Printf("\0216[%02x]\0217", (int) c); else Printf("%c", c); } } /* Ensure the next output item is one blank line after the buffer */ /* contents (aesthetics and clarity) */ Printf("\n\n"); } /*************************************************/ /* trace_tag_name() */ /* */ /* Converts a tag number (enum tag_no) to */ /* textual form (why isn't there a built in */ /* function to do this?). */ /* */ /* Parameters: Tag number. */ /* */ /* Returns: Pointer to a statically allocated */ /* name. */ /*************************************************/ static const char * trace_tag_name(tag_no tagno) { switch (tagno) { case TAG_NONE: return "none"; case TAG_A: return "A"; case TAG_ADDRESS: return "ADDRESS"; case TAG_BOLD: return "BOLD"; case TAG_BASE: return "BASE"; case TAG_BASEFONT: return "BASEFONT"; case TAG_BLOCKQUOTE: return "BLOCKQUOTE"; case TAG_BR: return "BR"; case TAG_CENTER: return "CENTER"; case TAG_CITE: return "CITE"; case TAG_CODE: return "CODE"; case TAG_DD: return "DD"; case TAG_DIR: return "DIR"; case TAG_DIV: return "DIV"; case TAG_DL: return "DL"; case TAG_DT: return "DT"; case TAG_DUMMY: return "DUMMY"; case TAG_EM: return "EM"; case TAG_FONT: return "FONT"; case TAG_FORM: return "FORM"; case TAG_H1: return "H1"; case TAG_H2: return "H2"; case TAG_H3: return "H3"; case TAG_H4: return "H4"; case TAG_H5: return "H5"; case TAG_H6: return "H6"; case TAG_HR: return "HR"; case TAG_ITALIC: return "ITALIC"; case TAG_IMG: return "IMG"; case TAG_INPUT: return "INPUT"; case TAG_ISINDEX: return "ISINDEX"; case TAG_KBD: return "KBD"; case TAG_LI: return "LI"; case TAG_LINK: return "LINK"; case TAG_MENU: return "MENU"; case TAG_META: return "META"; case TAG_OL: return "OL"; case TAG_OPTION: return "OPTION"; case TAG_P: return "P"; case TAG_PRE: return "PRE"; case TAG_SAMP: return "SAMP"; case TAG_SCRIPT: return "SCRIPT"; case TAG_SELECT: return "SELECT"; case TAG_STRIKE: return "STRIKE"; case TAG_STRONG: return "STRONG"; case TAG_STYLE: return "STYLE"; case TAG_TABLE: return "TABLE"; case TAG_TD: return "TD"; case TAG_TEXTAREA: return "TEXTAREA"; case TAG_TITLE: return "TITLE"; case TAG_TH: return "TH"; case TAG_TR: return "TR"; case TAG_TT: return "TT"; case TAG_U: return "U"; case TAG_UL: return "UL"; case TAG_VAR: return "VAR"; case TAG_XMP: return "XMP"; case TAG_FRAME: return "FRAME"; case TAG_BODY: return "BODY"; case TAG_FRAMESET: return "FRAMESET"; case TAG_HEAD: return "HEAD"; case TAG_NOFRAMES: return "NOFRAMES"; case TAG_SUB: return "SUB"; case TAG_SUP: return "SUP"; case TAG_AREA: return "AREA"; case TAG_MAP: return "MAP"; case TAG_OBJECT: return "OBJECT"; case TAG_PARAM: return "PARAM"; case TAG_BIG: return "BIG"; case TAG_SMALL: return "SMALL"; case TAG_APPLET: return "APPLET"; case TAG_FORM_END: return "/FORM"; default: return "unknown"; } } /*************************************************/ /* trace_add_description() */ /* */ /* Used to add a description to a list of other */ /* descriptions. If the first item, the text is */ /* merely placed in a new block; subsequent */ /* items are concatenated onto the existing list */ /* preceeded by a comma and a space. */ /* */ /* Parameters: Pointer to the malloc block, or */ /* NULL if nothing has been */ /* allocated yet; */ /* */ /* Pointer to a statically allocated */ /* description string. */ /* */ /* Returns: Pointer to the malloc block, */ /* which may have moved. */ /*************************************************/ static char * trace_add_description(char * list, const char * description) { if (list) { char * new = realloc(list, strlen(list) + 3); /* 3 = comma, space, terminator */ if (new) { list = new; strcat(list, ", "); new = realloc(list, strlen(list) + strlen(description) + 1); if (new) { list = new; strcat(list, description); } } } else { list = malloc(strlen(description) + 1); if (list) strcpy(list, description); } return list; } /*************************************************/ /* trace_style_bits() */ /* */ /* Converts a style word to textual form. */ /* */ /* Parameters: Pointer to the token to examine. */ /* */ /* Returns: Pointer to a comma separated list */ /* of names, in a malloced block */ /* which the caller is responsible */ /* for freeing. NULL may be returned */ /* if the initial malloc fails. */ /*************************************************/ static char * trace_style_bits(HStream * t) { char * list = NULL; unsigned int rems = t->style; if (ISHEAD(t)) { if (t->style & LINK) rems &= (~LINK), list = trace_add_description(list, "<LIST>"); if (t->style & ISINDEX) rems &= (~ISINDEX), list = trace_add_description(list, "<ISINDEX>"); if (t->style & META) rems &= (~META), list = trace_add_description(list, "<META>"); if (rems) { char desc[32]; sprintf(desc, "Unknown remainder 0x%08x", (int) rems); list = trace_add_description(list, desc); } } else if (ISFRAMESET(t)) { if (t->style & FRAME) rems &= (~FRAME), list = trace_add_description(list, "<FRAME>"); if (rems) { char desc[32]; sprintf(desc, "Unknown remainder 0x%08x", (int) rems); list = trace_add_description(list, desc); } } else if (ISBODY(t)) { /* General flags */ if (t->style & BOLD) rems &= (~BOLD), list = trace_add_description(list, "<BOLD>"); if (t->style & ITALIC) rems &= (~ITALIC), list = trace_add_description(list, "<ITALIC>"); if (t->style & TT) rems &= (~TT), list = trace_add_description(list, "<TT>"); if (t->style & A) rems &= (~A), list = trace_add_description(list, "<A>"); if (t->style & IMG) rems &= (~IMG), list = trace_add_description(list, "<IMG>"); if (t->style & P) rems &= (~P), list = trace_add_description(list, "<P>"); if (t->style & BR) rems &= (~BR), list = trace_add_description(list, "<BR>"); if (t->style & HR) rems &= (~HR), list = trace_add_description(list, "<HR>"); if (t->style & PRE) rems &= (~PRE), list = trace_add_description(list, "<PRE>"); if (t->style & DL) rems &= (~DL), list = trace_add_description(list, "<DL>"); if (t->style & DT) rems &= (~DT), list = trace_add_description(list, "<DT>"); if (t->style & DD) rems &= (~DD), list = trace_add_description(list, "<DD>"); if (t->style & UL) rems &= (~UL), list = trace_add_description(list, "<UL>"); if (t->style & LI) rems &= (~LI), list = trace_add_description(list, "<LI>"); if (t->style & BLOCKQUOTE) rems &= (~BLOCKQUOTE), list = trace_add_description(list, "<BLOCKQUOTE>"); if (t->style & ADDRESS) rems &= (~ADDRESS), list = trace_add_description(list, "<ADDRESS>"); if (t->style & CENTER) rems &= (~CENTER), list = trace_add_description(list, "<CENTER>"); if (t->style & FONT) rems &= (~FONT), list = trace_add_description(list, "<FONT>"); if (t->style & UNDERLINE) rems &= (~UNDERLINE), list = trace_add_description(list, "<UNDERLINE>"); if (t->style & STRIKE) rems &= (~STRIKE), list = trace_add_description(list, "<STRIKE>"); if (t->style & FORM) rems &= (~FORM), list = trace_add_description(list, "<FORM>"); if (t->style & RIGHT) rems &= (~RIGHT), list = trace_add_description(list, "<RIGHT>"); if (t->style & SUB) rems &= (~SUB), list = trace_add_description(list, "<SUB>"); if (t->style & SUP) rems &= (~SUP), list = trace_add_description(list, "<SUP>"); if (t->style & NOBR) rems &= (~NOBR), list = trace_add_description(list, "<NOBR>"); /* This area has got fairly messy due to various table */ /* implementations and so forth */ if (t->style & PCDATA) rems &= (~PCDATA), list = trace_add_description(list, "(PCDATA)"); /* Heading items */ if ((t->style & H_MASK) == H1) rems &= (~H_MASK), list = trace_add_description(list, "<H1>"); if ((t->style & H_MASK) == H2) rems &= (~H_MASK), list = trace_add_description(list, "<H2>"); if ((t->style & H_MASK) == H3) rems &= (~H_MASK), list = trace_add_description(list, "<H3>"); if ((t->style & H_MASK) == H4) rems &= (~H_MASK), list = trace_add_description(list, "<H4>"); if ((t->style & H_MASK) == H5) rems &= (~H_MASK), list = trace_add_description(list, "<H5>"); if ((t->style & H_MASK) == H6) rems &= (~H_MASK), list = trace_add_description(list, "<H6>"); if (rems) { char desc[32]; sprintf(desc, "Unknown remainder 0x%08x", (int) rems); list = trace_add_description(list, desc); } /* These currently map back to existing items so they don't need including */ /* separately (as every TT item would say TT, CODE, SAMP... etc. - they */ /* are left here to make it easier to add them to the above code should a */ /* distinct bit get allocated. */ // if (t->style & EM) rems &= (~EM), list = trace_add_description(list, "<EM -> ITALIC>"); // if (t->style & STRONG) rems &= (~STRONG), list = trace_add_description(list, "<STRONG -> BOLD>"); // if (t->style & CODE) rems &= (~CODE), list = trace_add_description(list, "<CODE -> TT>"); // if (t->style & SAMP) rems &= (~SAMP), list = trace_add_description(list, "<SAMP -> TT>"); // if (t->style & KBD) rems &= (~KBD), list = trace_add_description(list, "<KBD -> TT>"); // if (t->style & VAR) rems &= (~VAR), list = trace_add_description(list, "<VAR -> TT>"); // if (t->style & CITE) rems &= (~CITE), list = trace_add_description(list, "<CITE -> ITALIC>"); } else list = trace_add_description(list, "(Not HEAD or BODY)"); return list; } #else /* This'll work, but it is more efficient not to link to */ /* Trace.o at all for non-debug builds, as if TRACE is */ /* undefined, absolutely nothing within Trace.o will be */ /* referenced (and nothing will #include Trace.h either). */ void trace_keep_compiler_happy(void) { int a; a = 0; } #endif