1 /*
2 * $LynxId: GridText.c,v 1.313 2018/05/15 20:43:41 tom Exp $
3 *
4 * Character grid hypertext object
5 * ===============================
6 */
7
8 #include <HTUtils.h>
9 #include <HTString.h>
10 #include <HTAccess.h>
11 #include <HTAnchor.h>
12 #include <HTParse.h>
13 #include <HTTP.h>
14 #include <HTAlert.h>
15 #include <HTCJK.h>
16 #include <HTFile.h>
17 #include <UCDefs.h>
18 #include <UCAux.h>
19 #include <HText.h>
20
21 #include <assert.h>
22
23 #include <GridText.h>
24 #include <LYCurses.h>
25 #include <LYUtils.h>
26 #include <LYStrings.h>
27 #include <LYStructs.h>
28 #include <LYGlobalDefs.h>
29 #include <LYGetFile.h>
30 #include <LYClean.h>
31 #include <LYMail.h>
32 #include <LYList.h>
33 #include <LYCharSets.h>
34 #include <LYCharUtils.h> /* LYUCTranslateBack... */
35 #include <UCMap.h>
36 #include <LYEdit.h>
37 #include <LYPrint.h>
38 #include <LYPrettySrc.h>
39 #include <LYSearch.h>
40 #include <TRSTable.h>
41 #include <LYHistory.h>
42 #ifdef EXP_CHARTRANS_AUTOSWITCH
43 #include <UCAuto.h>
44 #endif /* EXP_CHARTRANS_AUTOSWITCH */
45
46 #include <LYexit.h>
47 #include <LYLeaks.h>
48
49 #ifdef USE_COLOR_STYLE
50 #include <AttrList.h>
51 #include <LYHash.h>
52 #include <LYStyle.h>
53
54 #endif
55
56 #include <LYJustify.h>
57
58 #define is_CJK2(b) (IS_CJK_TTY && is8bits(UCH(b)))
59
60 #ifdef USE_CURSES_PADS
61 # define DISPLAY_COLS (LYwideLines ? MAX_COLS : LYcols)
62 # define WRAP_COLS(text) ((text)->stbl ? \
63 (LYtableCols <= 0 \
64 ? DISPLAY_COLS \
65 : (LYtableCols * LYcols)/12) - LYbarWidth \
66 : LYcolLimit)
67 #else
68 # define DISPLAY_COLS LYcols
69 # define WRAP_COLS(text) LYcolLimit
70 #endif
71
72 #define FirstHTLine(text) ((text)->last_line->next)
73 #define LastHTLine(text) ((text)->last_line)
74
75 static void HText_trimHightext(HText *text, int final, int stop_before);
76
77 #define IS_UTF_EXTRA(ch) (text->T.output_utf8 && \
78 (UCH((ch))&0xc0) == 0x80)
79
80 #define IS_UTF8_EXTRA(ch) (!(text && text->T.output_utf8) || \
81 !is8bits(ch) || \
82 (UCH(line->data[i] & 0xc0) == 0xc0))
83
84 /* a test in compact form: how many extra UTF-8 chars after initial? - kw */
85 #define UTF8_XNEGLEN(c) (c&0xC0? 0 :c&32? 1 :c&16? 2 :c&8? 3 :c&4? 4 :c&2? 5:0)
86 #define UTF_XLEN(c) UTF8_XNEGLEN(((char)~(c)))
87
88 #ifdef KANJI_CODE_OVERRIDE
89 HTkcode last_kcode = NOKANJI; /* 1997/11/14 (Fri) 09:09:26 */
90 #endif
91
92 #undef CHAR_WIDTH
93
94 #ifdef CJK_EX
95 #define CHAR_WIDTH 6
96 #else
97 #define CHAR_WIDTH 1
98 #endif
99
100 /* Exports
101 */
102 HText *HTMainText = NULL; /* Equivalent of main window */
103 HTParentAnchor *HTMainAnchor = NULL; /* Anchor for HTMainText */
104
105 const char *HTAppName = LYNX_NAME; /* Application name */
106 const char *HTAppVersion = LYNX_VERSION; /* Application version */
107
108 static int HTFormNumber = 0;
109 static int HTFormFields = 0;
110 static char *HTCurSelectGroup = NULL; /* Form select group name */
111 static int HTCurSelectGroupCharset = -1; /* ... and name's charset */
112 int HTCurSelectGroupType = F_RADIO_TYPE; /* Group type */
113 char *HTCurSelectGroupSize = NULL; /* Length of select */
114 static char *HTCurSelectedOptionValue = NULL; /* Select choice */
115
116 const char *checked_box = "[X]";
117 const char *unchecked_box = "[ ]";
118 const char *checked_radio = "(*)";
119 const char *unchecked_radio = "( )";
120
121 static BOOLEAN underline_on = FALSE;
122 static BOOLEAN bold_on = FALSE;
123
124 #ifdef USE_SOURCE_CACHE
125 int LYCacheSource = SOURCE_CACHE_NONE;
126 int LYCacheSourceForAborted = SOURCE_CACHE_FOR_ABORTED_DROP;
127 #endif
128
129 #ifdef USE_SCROLLBAR
130 BOOLEAN LYShowScrollbar = FALSE;
131 BOOLEAN LYsb_arrow = TRUE;
132 int LYsb_begin = -1;
133 int LYsb_end = -1;
134 #endif
135
136 #ifndef VMS /* VMS has a better way - right? - kw */
137 #define CHECK_FREE_MEM
138 #endif
139
140 #ifdef CHECK_FREE_MEM
141 static void *LY_check_calloc(size_t nmemb, size_t size);
142
143 #define LY_CALLOC LY_check_calloc
144 #else
145 /* using the regular calloc */
146 #define LY_CALLOC calloc
147 #endif
148
149 /*
150 * The HTPool.data[] array has to align the same as malloc() would, to make the
151 * ALLOC_POOL scheme portable. For many platforms, that is the same as the
152 * number of bytes in a pointer. It may be larger, e.g., on machines which
153 * have more stringent requirements for floating point. 32-bits are plenty for
154 * representing styles, but we may need 64-bit or 128-bit alignment.
155 *
156 * The real issue is that performance is degraded if the alignment is not met,
157 * and some platforms such as Tru64 generate lots of warning messages.
158 */
159 #ifndef ALIGN_SIZE
160 #define ALIGN_SIZE sizeof(double)
161 #endif
162
163 #define BITS_DIR 2
164 #define BITS_POS 14
165
166 #define MASK_DIR ((1U << BITS_DIR) - 1)
167 #define CAST_DIR(n) ((MASK_DIR) & (unsigned)(n))
168
169 #define MASK_POS ((1U << BITS_POS) - 1)
170 #define CAST_POS(n) ((MASK_POS) & (unsigned)(n))
171
172 typedef struct {
173 unsigned sc_direction:BITS_DIR; /* on or off */
174 unsigned sc_horizpos:BITS_POS; /* horizontal position of this change */
175 unsigned sc_style:16; /* which style to change to */
176 } HTStyleChange;
177
178 #if defined(USE_COLOR_STYLE)
179 #define MAX_STYLES_ON_LINE 64
180 /* buffers used when current line is being aggregated, in split_line() */
181 static HTStyleChange stylechanges_buffers[2][MAX_STYLES_ON_LINE];
182 #endif
183
184 typedef HTStyleChange pool_data;
185
186 enum {
187 POOL_SIZE = ((8192
188 - 4 * sizeof(void *)
189 - sizeof(struct _HTPool *)
190 - sizeof(int))
191 / sizeof(pool_data))
192 };
193
194 typedef struct _HTPool {
195 pool_data data[POOL_SIZE];
196 struct _HTPool *prev;
197 unsigned used;
198 } HTPool;
199
200 /************************************************************************
201 These are generic macros for any pools (provided those structures have the
202 same members as HTPool). Pools are used for allocation of groups of
203 objects of the same type T. Pools are represented as a list of structures of
204 type P (called pool chunks here). Structure P has an array of N objects of
205 type T named 'data' (the number N in the array can be chosen arbitrary),
206 pointer to the previous pool chunk named 'prev', and the number of used items
207 in that pool chunk named 'used'. Here is a definition of the structure P:
208 struct P
209 {
210 T data[N];
211 struct P* prev;
212 int used;
213 };
214 It's recommended that sizeof(P) be memory page size minus 32 in order malloc'd
215 chunks to fit in machine page size.
216 Allocation of 'n' items in the pool is implemented by incrementing member
217 'used' by 'n' if (used+n <= N), or malloc a new pool chunk and
218 allocating 'n' items in that new chunk. It's the task of the programmer to
219 assert that 'n' is <= N. Only entire pool may be freed - this limitation makes
220 allocation algorithms trivial and fast - so the use of pools is limited to
221 objects that are freed in batch, that are not deallocated not in the batch, and
222 not reallocated.
223 Pools greatly reduce memory fragmentation and memory allocation/deallocation
224 speed due to the simple algorithms used. Due to the fact that memory is
225 'allocated' in array, alignment overhead is minimal. Allocating strings in a
226 pool provided their length will never exceed N and is much smaller than N seems
227 to be very efficient.
228 [Several types of memory-hungry objects are stored in the pool now: styles,
229 lines, anchors, and FormInfo. Arrays of HTStyleChange are stored as is,
230 other objects are stored using a cast.]
231
232 Pool is referenced by the pointer to the last chunk that contains free slots.
233 Functions that allocate memory in the pool update that pointer if needed.
234 There are 3 functions - POOL_NEW, POOL_FREE, and ALLOC_IN_POOL.
235
236 - VH
237
238 *************************************************************************/
239
240 #define POOLallocstyles(ptr, n) ptr = ALLOC_IN_POOL(&HTMainText->pool, (unsigned) ((n) * sizeof(pool_data)))
241 #define POOLallocHTLine(ptr, size) ptr = (HTLine*) ALLOC_IN_POOL(&HTMainText->pool, (unsigned) LINE_SIZE(size))
242 #define POOLallocstring(ptr, len) ptr = (char*) ALLOC_IN_POOL(&HTMainText->pool, (unsigned) ((len) + 1))
243 #define POOLtypecalloc(T, ptr) ptr = (T*) ALLOC_IN_POOL(&HTMainText->pool, (unsigned) sizeof(T))
244
245 /**************************************************************************/
246 /*
247 * Allocates 'n' items in the pool of type 'HTPool' pointed by 'poolptr'.
248 * Returns a pointer to the "allocated" memory if successful.
249 * Updates 'poolptr' if necessary.
250 */
ALLOC_IN_POOL(HTPool ** ppoolptr,unsigned request)251 static void *ALLOC_IN_POOL(HTPool ** ppoolptr, unsigned request)
252 {
253 HTPool *pool = *ppoolptr;
254 pool_data *ptr;
255 unsigned n;
256 unsigned j;
257
258 if (!pool) {
259 outofmem(__FILE__, "ALLOC_IN_POOL");
260 } else {
261 n = request;
262 if (n == 0)
263 n = 1;
264 j = (n % ALIGN_SIZE);
265 if (j != 0)
266 n += (unsigned) (ALIGN_SIZE - j);
267 n /= sizeof(pool_data);
268
269 if (POOL_SIZE >= (pool->used + n)) {
270 ptr = pool->data + pool->used;
271 pool->used += n;
272 } else {
273 HTPool *newpool = (HTPool *) LY_CALLOC((size_t) 1, sizeof(HTPool));
274
275 if (!newpool) {
276 outofmem(__FILE__, "ALLOC_IN_POOL");
277 } else {
278 newpool->prev = pool;
279 newpool->used = n;
280 ptr = newpool->data;
281 *ppoolptr = newpool;
282 }
283 }
284 }
285 return ptr;
286 }
287
288 /*
289 * Returns a pointer to initialized pool of type 'HTPool', or NULL if fails.
290 */
POOL_NEW(void)291 static HTPool *POOL_NEW(void)
292 {
293 HTPool *poolptr = (HTPool *) LY_CALLOC((size_t) 1, sizeof(HTPool));
294
295 if (poolptr) {
296 poolptr->prev = NULL;
297 poolptr->used = 0;
298 }
299 return poolptr;
300 }
301
302 /*
303 * Frees a pool of type 'HTPool' pointed by poolptr.
304 */
POOL_FREE(HTPool * poolptr)305 static void POOL_FREE(HTPool * poolptr)
306 {
307 HTPool *cur = poolptr;
308 HTPool *prev;
309
310 while (cur) {
311 prev = cur->prev;
312 free(cur);
313 cur = prev;
314 }
315 }
316
317 /**************************************************************************/
318 /**************************************************************************/
319
320 typedef struct _line {
321 struct _line *next;
322 struct _line *prev;
323 unsigned short offset; /* Implicit initial spaces */
324 unsigned short size; /* Number of characters */
325 #if defined(USE_COLOR_STYLE)
326 HTStyleChange *styles;
327 unsigned short numstyles;
328 #endif
329 char data[1]; /* Space for terminator at least! */
330 } HTLine;
331
332 /* Allow for terminator */
333 #define LINE_SIZE(size) (sizeof(HTLine) + (size_t)(size))
334
335 #ifndef HTLINE_NOT_IN_POOL
336 #define HTLINE_NOT_IN_POOL 0 /* debug with this set to 1 */
337 #endif
338
339 #if HTLINE_NOT_IN_POOL
340 #define allocHTLine(ptr, size) { ptr = (HTLine *)calloc(1, LINE_SIZE(size)); }
341 #define freeHTLine(self, ptr) { \
342 if (ptr && ptr != TEMP_LINE(self, 0) && ptr != TEMP_LINE(self, 1)) \
343 FREE(ptr); \
344 }
345 #else
346 #define allocHTLine(ptr, size) POOLallocHTLine(ptr, size)
347 #define freeHTLine(self, ptr) {}
348 #endif
349
350 /*
351 * Last line buffer; the second is used in split_line(). Not in pool!
352 * We cannot wrap in middle of multibyte sequences, so allocate 2 extra
353 * for a workspace. This is stored in the HText, to prevent confusion
354 * between different documents. Note also that it is declared with an
355 * HTLine at the beginning so pointers will be properly aligned.
356 */
357 typedef struct {
358 HTLine base;
359 char data[MAX_LINE + 2];
360 } HTLineTemp;
361
362 #define TEMP_LINE(p,n) ((HTLine *)&(p->temp_line[n]))
363
364 typedef struct _TextAnchor {
365 struct _TextAnchor *next;
366 struct _TextAnchor *prev; /* www_user_search only! */
367 int sgml_offset; /* used for updating position after reparsing */
368 int number; /* For user interface */
369 int show_number; /* For user interface (unique-urls) */
370 int line_num; /* Place in document */
371 short line_pos; /* Bytes/chars - extent too */
372 short extent; /* (see HText_trimHightext) */
373 BOOL show_anchor; /* Show the anchor? */
374 BOOL inUnderline; /* context is underlined */
375 BOOL expansion_anch; /* TEXTAREA edit new anchor */
376 char link_type; /* Normal, internal, or form? */
377 FormInfo *input_field; /* Info for form links */
378 HiliteList lites;
379
380 HTChildAnchor *anchor;
381 } TextAnchor;
382
383 typedef struct {
384 char *name; /* ID value of TAB */
385 int column; /* Zero-based column value */
386 } HTTabID;
387
388 typedef enum {
389 S_text,
390 S_esc,
391 S_dollar,
392 S_paren,
393 S_nonascii_text,
394 S_dollar_paren,
395 S_jisx0201_text
396 } eGridState; /* Escape sequence? */
397
398 #ifdef USE_TH_JP_AUTO_DETECT
399 typedef enum { /* Detected Kanji code */
400 DET_SJIS,
401 DET_EUC,
402 DET_NOTYET,
403 DET_MIXED
404 } eDetectedKCode;
405
406 typedef enum {
407 SJIS_state_neutral,
408 SJIS_state_in_kanji,
409 SJIS_state_has_bad_code
410 } eSJIS_status;
411
412 typedef enum {
413 EUC_state_neutral,
414 EUC_state_in_kanji,
415 EUC_state_in_kana,
416 EUC_state_has_bad_code
417 } eEUC_status;
418 #endif
419
420 /* Notes on struct _HText:
421 * next_line is valid if stale is false.
422 * top_of_screen line means the line at the top of the screen
423 * or just under the title if there is one.
424 */
425 struct _HText {
426 HTParentAnchor *node_anchor;
427
428 HTLine *last_line;
429 HTLineTemp temp_line[2];
430 int Lines; /* Number of them */
431 TextAnchor *first_anchor; /* double-linked on demand */
432 TextAnchor *last_anchor;
433 TextAnchor *last_anchor_before_stbl;
434 TextAnchor *last_anchor_before_split;
435 HTList *forms; /* also linked internally */
436 int last_anchor_number; /* user number */
437 BOOL source; /* Is the text source? */
438 BOOL toolbar; /* Toolbar set? */
439 HTList *tabs; /* TAB IDs */
440 HTList *hidden_links; /* Content-less links ... */
441 int hiddenlinkflag; /* ... and how to treat them */
442 BOOL no_cache; /* Always refresh? */
443 char LastChar; /* For absorbing white space */
444
445 /* For Internal use: */
446 HTStyle *style; /* Current style */
447 int display_on_the_fly; /* Lines left */
448 int top_of_screen; /* Line number */
449 HTLine *top_of_screen_line; /* Top */
450 HTLine *next_line; /* Bottom + 1 */
451 unsigned permissible_split; /* in last line */
452 BOOL in_line_1; /* of paragraph */
453 BOOL stale; /* Must refresh */
454 BOOL page_has_target; /* has target on screen */
455 BOOL has_utf8; /* has utf-8 on screen or line */
456 BOOL had_utf8; /* had utf-8 when last displayed */
457 int next_number; /* next a->number value */
458 #ifdef DISP_PARTIAL
459 int first_lineno_last_disp_partial;
460 int last_lineno_last_disp_partial;
461 #endif
462 STable_info *stbl;
463 HTList *enclosed_stbl;
464
465 HTkcode kcode; /* Kanji code? */
466 HTkcode specified_kcode; /* Specified Kanji code */
467 #ifdef USE_TH_JP_AUTO_DETECT
468 eDetectedKCode detected_kcode;
469 eSJIS_status SJIS_status;
470 eEUC_status EUC_status;
471 #endif
472 eGridState state; /* Escape sequence? */
473 int kanji_buf; /* Lead multibyte */
474 int in_sjis; /* SJIS flag */
475 int halted; /* emergency halt */
476
477 BOOL have_8bit_chars; /* Any non-ASCII chars? */
478 LYUCcharset *UCI; /* node_anchor UCInfo */
479 int UCLYhndl; /* charset we are fed */
480 UCTransParams T;
481
482 HTStream *target; /* Output stream */
483 HTStreamClass targetClass; /* Output routines */
484
485 HTPool *pool; /* this HText memory pool */
486
487 #ifdef USE_SOURCE_CACHE
488 /*
489 * Parse settings when this HText was generated.
490 */
491 BOOL clickable_images;
492 BOOL pseudo_inline_alts;
493 BOOL verbose_img;
494 BOOL raw_mode;
495 BOOL historical_comments;
496 BOOL minimal_comments;
497 BOOL soft_dquotes;
498 short old_dtd;
499 short keypad_mode;
500 short disp_lines; /* Screen size */
501 short disp_cols; /* Used for reports only */
502 #endif
503 };
504
505 /* exported */
HText_pool_calloc(HText * text,unsigned size)506 void *HText_pool_calloc(HText *text, unsigned size)
507 {
508 return (void *) ALLOC_IN_POOL(&text->pool, size);
509 }
510
511 static void HText_AddHiddenLink(HText *text, TextAnchor *textanchor);
512
513 #ifdef USE_JUSTIFY_ELTS
514 BOOL can_justify_here;
515 BOOL can_justify_here_saved;
516
517 BOOL can_justify_this_line; /* =FALSE if line contains form objects */
518 int wait_for_this_stacked_elt; /* -1 if can justify contents of the
519
520 element on the op of stack. If positive - specifies minimal stack depth
521 plus 1 at which we can justify element (can be MAX_LINE+2 if
522 ok_justify ==FALSE or in psrcview. */
523 BOOL form_in_htext; /*to indicate that we are in form (since HTML_FORM is
524
525 not stacked in the HTML.c */
526 BOOL in_DT = FALSE;
527
528 #ifdef DEBUG_JUSTIFY
529 BOOL can_justify_stack_depth; /* can be 0 or 1 if all code is correct */
530 #endif
531
532 typedef struct {
533 int byte_len; /*length in bytes */
534 int cell_len; /*length in cells */
535 } ht_run_info;
536
537 static int justify_start_position; /* this is an index of char from which
538
539 justification can start (eg after "* " preceeding <li> text) */
540
541 static int ht_num_runs; /*the number of runs filled */
542 static ht_run_info ht_runs[MAX_LINE];
543 static BOOL this_line_was_split;
544 static TextAnchor *last_anchor_of_previous_line;
545 static BOOL have_raw_nbsps = FALSE;
546
ht_justify_cleanup(void)547 void ht_justify_cleanup(void)
548 {
549 wait_for_this_stacked_elt = !ok_justify
550 # ifdef USE_PRETTYSRC
551 || psrc_view
552 # endif
553 ? 30000 /*MAX_NESTING */ + 2 /*some unreachable value */
554 : -1;
555 can_justify_here = TRUE;
556 can_justify_this_line = TRUE;
557 form_in_htext = FALSE;
558
559 last_anchor_of_previous_line = NULL;
560 this_line_was_split = FALSE;
561 in_DT = FALSE;
562 have_raw_nbsps = FALSE;
563 }
564
mark_justify_start_position(void * text)565 void mark_justify_start_position(void *text)
566 {
567 if (text && ((HText *) text)->last_line)
568 justify_start_position = ((HText *) text)->last_line->size;
569 }
570
571 #define REALLY_CAN_JUSTIFY(text) ( (wait_for_this_stacked_elt<0) && \
572 ( text->style->alignment == HT_LEFT || \
573 text->style->alignment == HT_JUSTIFY) && \
574 !IS_CJK_TTY && !in_DT && \
575 can_justify_here && can_justify_this_line && !form_in_htext )
576
577 #endif /* USE_JUSTIFY_ELTS */
578
579 /*
580 * Boring static variable used for moving cursor across
581 */
582 #define UNDERSCORES(n) \
583 ((n) >= MAX_LINE ? underscore_string : &underscore_string[(MAX_LINE-1)] - (n))
584
585 static char underscore_string[MAX_LINE + 1];
586 char star_string[MAX_LINE + 1];
587
588 static int ctrl_chars_on_this_line = 0; /* num of ctrl chars in current line */
589 static int utfxtra_on_this_line = 0; /* num of UTF-8 extra bytes in line,
590
591 they *also* count as ctrl chars. */
592 #ifdef WIDEC_CURSES
593 #define UTFXTRA_ON_THIS_LINE 0
594 #else
595 #define UTFXTRA_ON_THIS_LINE utfxtra_on_this_line
596 #endif
597
598 static HTStyle default_style =
599 {0, NULL, "(Unstyled)", 0, NULL, "",
600 (HTFont) 0, 1, HT_BLACK, 0, 0,
601 0, 0, 0, HT_LEFT, 1, 0, 0,
602 NO, NO, 0, 0, 0};
603
604 static HTList *loaded_texts = NULL; /* A list of all those in memory */
605 HTList *search_queries = NULL; /* isindex and whereis queries */
606
607 #ifdef LY_FIND_LEAKS
608 static void free_all_texts(void);
609 #endif
610
611 static BOOL HText_TrueEmptyLine(HTLine *line, HText *text, int IgnoreSpaces);
612
613 static int HText_TrueLineSize(HTLine *line, HText *text, int IgnoreSpaces);
614
615 #ifdef CHECK_FREE_MEM
616
617 /*
618 * text->halted = 1: have set fake 'Z' and output a message
619 * 2: next time when HText_appendCharacter is called
620 * it will append *** MEMORY EXHAUSTED ***, then set
621 * to 3.
622 * 3: normal text output will be suppressed (but not anchors,
623 * form fields etc.)
624 */
HText_halt(void)625 static void HText_halt(void)
626 {
627 if (HTFormNumber > 0)
628 HText_DisableCurrentForm();
629 if (!HTMainText)
630 return;
631 if (HTMainText->halted < 2)
632 HTMainText->halted = 2;
633 }
634
635 #define MIN_NEEDED_MEM 5000
636
637 /*
638 * Check whether factor*min(bytes,MIN_NEEDED_MEM) is available,
639 * or bytes if factor is 0.
640 * MIN_NEEDED_MEM and factor together represent a security margin,
641 * to take account of all the memory allocations where we don't check
642 * and of buffers which may be emptied before HTCheckForInterupt()
643 * is (maybe) called and other things happening, with some chance of
644 * success.
645 * This just tries to malloc() the to-be-checked-for amount of memory,
646 * which might make the situation worse depending how allocation works.
647 * There should be a better way... - kw
648 */
mem_is_avail(int factor,size_t bytes)649 static BOOL mem_is_avail(int factor, size_t bytes)
650 {
651 void *p;
652
653 if (bytes < MIN_NEEDED_MEM && factor > 0)
654 bytes = MIN_NEEDED_MEM;
655 if (factor == 0)
656 factor = 1;
657 p = malloc((size_t) factor * bytes);
658 if (p) {
659 FREE(p);
660 return YES;
661 } else {
662 return NO;
663 }
664 }
665
666 /*
667 * Replacement for calloc which checks for "enough" free memory
668 * (with some security margins) and tries various recovery actions
669 * if deemed necessary. - kw
670 */
LY_check_calloc(size_t nmemb,size_t size)671 static void *LY_check_calloc(size_t nmemb, size_t size)
672 {
673 int i, n;
674
675 if (mem_is_avail(4, nmemb * size)) {
676 return (calloc(nmemb, size));
677 }
678 n = HTList_count(loaded_texts);
679 for (i = n - 1; i > 0; i--) {
680 HText *t = (HText *) HTList_objectAt(loaded_texts, i);
681
682 CTRACE((tfp,
683 "\nBUG *** Emergency freeing document %d/%d for '%s'%s!\n",
684 i + 1, n,
685 ((t && t->node_anchor &&
686 t->node_anchor->address) ?
687 t->node_anchor->address : "unknown anchor"),
688 ((t && t->node_anchor &&
689 t->node_anchor->post_data) ?
690 " with POST data" : "")));
691 HTList_removeObjectAt(loaded_texts, i);
692 HText_free(t);
693 if (mem_is_avail(4, nmemb * size)) {
694 return (calloc(nmemb, size));
695 }
696 }
697 LYFakeZap(YES);
698 if (!HTMainText || HTMainText->halted <= 1) {
699 if (!mem_is_avail(2, nmemb * size)) {
700 HText_halt();
701 if (mem_is_avail(0, (size_t) 700)) {
702 HTAlert(gettext("Memory exhausted, display interrupted!"));
703 }
704 } else {
705 if ((!HTMainText || HTMainText->halted == 0) &&
706 mem_is_avail(0, (size_t) 700)) {
707 HTAlert(gettext("Memory exhausted, will interrupt transfer!"));
708 if (HTMainText)
709 HTMainText->halted = 1;
710 }
711 }
712 }
713 return (calloc(nmemb, size));
714 }
715
716 #endif /* CHECK_FREE_MEM */
717
718 #ifdef USE_COLOR_STYLE
719 /*
720 * Color style information is stored with the multibyte-character offset into
721 * the string at which the style would apply. Compute the corresponding column
722 * so we can compare it with the updated column value after writing strings
723 * with curses.
724 *
725 * The offsets count multibyte characters. Other parts of the code assume each
726 * character uses one cell, but some CJK (or UTF-8) codes use two cells. We
727 * need to know the number of cells.
728 */
StyleToCols(HText * text,HTLine * line,int nstyle)729 static int StyleToCols(HText *text, HTLine *line, int nstyle)
730 {
731 int result = line->offset; /* this much is spaces one byte/cell */
732 int nchars = line->styles[nstyle].sc_horizpos;
733 char *data = line->data;
734 char *last = line->size + data;
735 int utf_extra;
736
737 while (nchars > 0 && data < last) {
738 if (IsSpecialAttrChar(*data) && *data != LY_SOFT_NEWLINE) {
739 ++data;
740 } else {
741 utf_extra = (int) utf8_length(text->T.output_utf8, data);
742 if (utf_extra++) {
743 result += LYstrExtent(data, utf_extra, 2);
744 data += utf_extra;
745 } else if (is_CJK2(*data)) {
746 data += 2;
747 result += 2;
748 } else {
749 ++data;
750 ++result;
751 }
752 --nchars;
753 }
754 }
755
756 return result;
757 }
758 #endif
759
760 /*
761 * Clear highlight information for a given anchor
762 * (text was allocated in the pool).
763 */
LYClearHiText(TextAnchor * a)764 static void LYClearHiText(TextAnchor *a)
765 {
766 FREE(a->lites.hl_info);
767
768 a->lites.hl_base.hl_text = NULL;
769 a->lites.hl_len = 0;
770 }
771
772 #define LYFreeHiText(a) FREE((a)->lites.hl_info)
773
774 /*
775 * Set the initial highlight information for a given anchor.
776 */
LYSetHiText(TextAnchor * a,const char * text,unsigned len)777 static void LYSetHiText(TextAnchor *a,
778 const char *text,
779 unsigned len)
780 {
781 if (text != NULL) {
782 POOLallocstring(a->lites.hl_base.hl_text, len + 1);
783 memcpy(a->lites.hl_base.hl_text, text, (size_t) len);
784 *(a->lites.hl_base.hl_text + len) = '\0';
785
786 a->lites.hl_len = 1;
787 }
788 }
789
790 /*
791 * Add highlight information for the next line of a anchor.
792 */
LYAddHiText(TextAnchor * a,const char * text,int x)793 static void LYAddHiText(TextAnchor *a,
794 const char *text,
795 int x)
796 {
797 HiliteInfo *have = a->lites.hl_info;
798 size_t need = (unsigned) (a->lites.hl_len - 1);
799 size_t want;
800
801 a->lites.hl_len = (short) (a->lites.hl_len + 1);
802 want = (size_t) (a->lites.hl_len) * sizeof(HiliteInfo);
803 if (have != NULL) {
804 have = (HiliteInfo *) realloc(have, want);
805 } else {
806 have = (HiliteInfo *) malloc(want);
807 }
808 a->lites.hl_info = have;
809
810 POOLallocstring(have[need].hl_text, strlen(text) + 1);
811 strcpy(have[need].hl_text, text);
812 have[need].hl_x = (short) x;
813 }
814
815 /*
816 * Return an offset to skip leading blanks in the highlighted link. That is
817 * needed to avoid having the color-style paint the leading blanks.
818 */
819 #ifdef USE_COLOR_STYLE
LYAdjHiTextPos(TextAnchor * a,int count)820 static int LYAdjHiTextPos(TextAnchor *a, int count)
821 {
822 char *result;
823
824 if (count >= a->lites.hl_len)
825 result = NULL;
826 else if (count > 0)
827 result = a->lites.hl_info[count - 1].hl_text;
828 else
829 result = a->lites.hl_base.hl_text;
830
831 return (result != 0) ? (int) (LYSkipBlanks(result) - result) : 0;
832 }
833
834 #else
835 #define LYAdjHiTextPos(a,count) 0
836 #endif
837
838 /*
839 * Get the highlight text, counting from zero.
840 */
LYGetHiTextStr(TextAnchor * a,int count)841 static char *LYGetHiTextStr(TextAnchor *a, int count)
842 {
843 char *result;
844
845 if (count >= a->lites.hl_len)
846 result = NULL;
847 else if (count > 0)
848 result = a->lites.hl_info[count - 1].hl_text;
849 else
850 result = a->lites.hl_base.hl_text;
851 result += LYAdjHiTextPos(a, count);
852 return result;
853 }
854
855 /*
856 * Get the X-ordinate at which to draw the corresponding highlight-text
857 */
LYGetHiTextPos(TextAnchor * a,int count)858 static int LYGetHiTextPos(TextAnchor *a, int count)
859 {
860 int result;
861
862 if (count >= a->lites.hl_len)
863 result = -1;
864 else if (count > 0)
865 result = a->lites.hl_info[count - 1].hl_x;
866 else
867 result = a->line_pos;
868 result += LYAdjHiTextPos(a, count);
869 return result;
870 }
871
872 /*
873 * Copy highlighting information from anchor 'b' to 'a'.
874 */
LYCopyHiText(TextAnchor * a,TextAnchor * b)875 static void LYCopyHiText(TextAnchor *a, TextAnchor *b)
876 {
877 int count;
878 char *s;
879
880 LYClearHiText(a);
881 for (count = 0;; ++count) {
882 if ((s = LYGetHiTextStr(b, count)) == NULL)
883 break;
884 if (count == 0) {
885 LYSetHiText(a, s, (unsigned) strlen(s));
886 } else {
887 LYAddHiText(a, s, LYGetHiTextPos(b, count));
888 }
889 }
890 }
891
HText_getChartransInfo(HText * me)892 static void HText_getChartransInfo(HText *me)
893 {
894 me->UCLYhndl = HTAnchor_getUCLYhndl(me->node_anchor, UCT_STAGE_HTEXT);
895 if (me->UCLYhndl < 0) {
896 int chndl = current_char_set;
897
898 HTAnchor_setUCInfoStage(me->node_anchor, chndl,
899 UCT_STAGE_HTEXT, UCT_SETBY_STRUCTURED);
900 me->UCLYhndl = HTAnchor_getUCLYhndl(me->node_anchor,
901 UCT_STAGE_HTEXT);
902 }
903 me->UCI = HTAnchor_getUCInfoStage(me->node_anchor, UCT_STAGE_HTEXT);
904 }
905
PerFormInfo_free(PerFormInfo * form)906 static void PerFormInfo_free(PerFormInfo * form)
907 {
908 if (form) {
909 FREE(form->data.submit_action);
910 FREE(form->data.submit_enctype);
911 FREE(form->data.submit_title);
912 FREE(form->accept_cs);
913 FREE(form->thisacceptcs);
914 FREE(form);
915 }
916 }
917
free_form_fields(FormInfo * input_field)918 static void free_form_fields(FormInfo * input_field)
919 {
920 /*
921 * Free form fields.
922 */
923 if (input_field->type == F_OPTION_LIST_TYPE &&
924 input_field->select_list != NULL) {
925 /*
926 * Free off option lists if present.
927 * It should always be present for F_OPTION_LIST_TYPE
928 * unless we had invalid markup which prevented
929 * HText_setLastOptionValue from finishing its job
930 * and left the input field in an insane state. - kw
931 */
932 OptionType *optptr = input_field->select_list;
933 OptionType *tmp;
934
935 while (optptr) {
936 tmp = optptr;
937 optptr = tmp->next;
938 FREE(tmp->name);
939 FREE(tmp->cp_submit_value);
940 FREE(tmp);
941 }
942 input_field->select_list = NULL;
943 /*
944 * Don't free the value field on option
945 * lists since it points to a option value
946 * same for orig value.
947 */
948 input_field->value = NULL;
949 input_field->orig_value = NULL;
950 input_field->cp_submit_value = NULL;
951 input_field->orig_submit_value = NULL;
952 } else {
953 FREE(input_field->value);
954 FREE(input_field->orig_value);
955 FREE(input_field->cp_submit_value);
956 FREE(input_field->orig_submit_value);
957 }
958 FREE(input_field->name);
959 FREE(input_field->submit_action);
960 FREE(input_field->submit_enctype);
961 FREE(input_field->submit_title);
962
963 FREE(input_field->accept_cs);
964 }
965
FormList_delete(HTList * forms)966 static void FormList_delete(HTList *forms)
967 {
968 HTList *cur = forms;
969 PerFormInfo *form;
970
971 while ((form = (PerFormInfo *) HTList_nextObject(cur)) != NULL)
972 PerFormInfo_free(form);
973 HTList_delete(forms);
974 }
975
976 #ifdef DISP_PARTIAL
ResetPartialLinenos(HText * text)977 static void ResetPartialLinenos(HText *text)
978 {
979 if (text != 0) {
980 text->first_lineno_last_disp_partial = -1;
981 text->last_lineno_last_disp_partial = -1;
982 }
983 }
984 #endif
985
986 /* Creation Method
987 * ---------------
988 */
HText_new(HTParentAnchor * anchor)989 HText *HText_new(HTParentAnchor *anchor)
990 {
991 #if defined(VMS) && defined(VAXC) && !defined(__DECC)
992 #include <lib$routines.h>
993 int status, VMType = 3, VMTotal;
994 #endif /* VMS && VAXC && !__DECC */
995 HTLine *line = NULL;
996 HText *self = typecalloc(HText);
997
998 if (!self)
999 outofmem(__FILE__, "HText_New");
1000
1001 CTRACE((tfp, "GridText: start HText_new\n"));
1002
1003 #if defined(VMS) && defined (VAXC) && !defined(__DECC)
1004 status = lib$stat_vm(&VMType, &VMTotal);
1005 CTRACE((tfp, "GridText: VMTotal = %d\n", VMTotal));
1006 #endif /* VMS && VAXC && !__DECC */
1007
1008 /*
1009 * If the previously shown text had UTF-8 characters on screen,
1010 * remember this in the newly created object. Do this now, before
1011 * the previous object may become invalid. - kw
1012 */
1013 if (HTMainText) {
1014 self->had_utf8 = HTMainText->has_utf8;
1015 HTMainText->has_utf8 = NO;
1016 }
1017
1018 if (!loaded_texts) {
1019 loaded_texts = HTList_new();
1020 #ifdef LY_FIND_LEAKS
1021 atexit(free_all_texts);
1022 #endif
1023 }
1024
1025 /*
1026 * Links between anchors & documents are a 1-1 relationship. If
1027 * an anchor is already linked to a document we didn't call
1028 * HTuncache_current_document(), so we'll check now
1029 * and free it before reloading. - Dick Wesseling (ftu@fi.ruu.nl)
1030 */
1031 if (anchor->document) {
1032 HTList_removeObject(loaded_texts, anchor->document);
1033 CTRACE((tfp, "GridText: Auto-uncaching\n"));
1034
1035 HTAnchor_delete_links(anchor);
1036 ((HText *) anchor->document)->node_anchor = NULL;
1037 HText_free((HText *) anchor->document);
1038 anchor->document = NULL;
1039 }
1040
1041 HTList_addObject(loaded_texts, self);
1042 #if defined(VMS) && defined(VAXC) && !defined(__DECC)
1043 while (HTList_count(loaded_texts) > HTCacheSize &&
1044 VMTotal > HTVirtualMemorySize)
1045 #else
1046 if (HTList_count(loaded_texts) > HTCacheSize)
1047 #endif /* VMS && VAXC && !__DECC */
1048 {
1049 CTRACE((tfp, "GridText: Freeing off cached doc.\n"));
1050 HText_free((HText *) HTList_removeFirstObject(loaded_texts));
1051 #if defined(VMS) && defined (VAXC) && !defined(__DECC)
1052 status = lib$stat_vm(&VMType, &VMTotal);
1053 CTRACE((tfp, "GridText: VMTotal reduced to %d\n", VMTotal));
1054 #endif /* VMS && VAXC && !__DECC */
1055 }
1056
1057 self->pool = POOL_NEW();
1058 if (!self->pool)
1059 outofmem(__FILE__, "HText_New");
1060
1061 line = self->last_line = TEMP_LINE(self, 0);
1062 line->next = line->prev = line;
1063 line->offset = line->size = 0;
1064 line->data[line->size] = '\0';
1065 #ifdef USE_COLOR_STYLE
1066 line->numstyles = 0;
1067 line->styles = stylechanges_buffers[0];
1068 #endif
1069 self->Lines = 0;
1070 self->first_anchor = self->last_anchor = NULL;
1071 self->last_anchor_before_split = NULL;
1072 self->style = &default_style;
1073 self->top_of_screen = 0;
1074 self->node_anchor = anchor;
1075 self->last_anchor_number = 0; /* Numbering of them for references */
1076 self->stale = YES;
1077 self->toolbar = NO;
1078 self->tabs = NULL;
1079 self->next_number = 1;
1080 #ifdef USE_SOURCE_CACHE
1081 /*
1082 * Remember the parse settings.
1083 */
1084 self->clickable_images = clickable_images;
1085 self->pseudo_inline_alts = pseudo_inline_alts;
1086 self->verbose_img = verbose_img;
1087 self->raw_mode = LYUseDefaultRawMode;
1088 self->historical_comments = historical_comments;
1089 self->minimal_comments = minimal_comments;
1090 self->soft_dquotes = soft_dquotes;
1091 self->old_dtd = (short) Old_DTD;
1092 self->keypad_mode = (short) keypad_mode;
1093 self->disp_lines = (short) LYlines;
1094 self->disp_cols = (short) DISPLAY_COLS;
1095 #endif
1096 /*
1097 * If we are going to render the List Page, always merge in hidden
1098 * links to get the numbering consistent if form fields are numbered
1099 * and show up as hidden links in the list of links.
1100 * If we are going to render a bookmark file, also always merge in
1101 * hidden links, to get the link numbers consistent with the counting
1102 * in remove_bookmark_link(). Normally a bookmark file shouldn't
1103 * contain any entries with empty titles, but it might happen. - kw
1104 */
1105 if (anchor->bookmark ||
1106 LYIsUIPage3(anchor->address, UIP_LIST_PAGE, 0) ||
1107 LYIsUIPage3(anchor->address, UIP_ADDRLIST_PAGE, 0))
1108 self->hiddenlinkflag = HIDDENLINKS_MERGE;
1109 else
1110 self->hiddenlinkflag = LYHiddenLinks;
1111 self->hidden_links = NULL;
1112 self->no_cache = (BOOLEAN) ((anchor->no_cache ||
1113 anchor->post_data)
1114 ? YES
1115 : NO);
1116 self->LastChar = '\0';
1117
1118 #ifndef USE_PRETTYSRC
1119 if (HTOutputFormat == WWW_SOURCE)
1120 self->source = YES;
1121 else
1122 self->source = NO;
1123 #else
1124 /* mark_htext_as_source == TRUE if we are parsing html file (and psrc_view
1125 * is set temporary to false at creation time)
1126 *
1127 * psrc_view == TRUE if source of the text produced by some lynx module
1128 * (like ftp browsers) is requested). - VH
1129 */
1130 self->source = (BOOL) (LYpsrc
1131 ? mark_htext_as_source || psrc_view
1132 : HTOutputFormat == WWW_SOURCE);
1133 mark_htext_as_source = FALSE;
1134 #endif
1135 HTAnchor_setDocument(anchor, (HyperDoc *) self);
1136 HTFormNumber = 0; /* no forms started yet */
1137 HTMainText = self;
1138 HTMainAnchor = anchor;
1139 self->display_on_the_fly = 0;
1140 self->kcode = NOKANJI;
1141 self->specified_kcode = NOKANJI;
1142 #ifdef USE_TH_JP_AUTO_DETECT
1143 self->detected_kcode = DET_NOTYET;
1144 self->SJIS_status = SJIS_state_neutral;
1145 self->EUC_status = EUC_state_neutral;
1146 #endif
1147 self->state = S_text;
1148 self->kanji_buf = '\0';
1149 self->in_sjis = 0;
1150 self->have_8bit_chars = NO;
1151 HText_getChartransInfo(self);
1152 UCSetTransParams(&self->T,
1153 self->UCLYhndl, self->UCI,
1154 current_char_set,
1155 &LYCharSet_UC[current_char_set]);
1156
1157 /*
1158 * Check the kcode setting if the anchor has a charset element. -FM
1159 */
1160 HText_setKcode(self, anchor->charset,
1161 HTAnchor_getUCInfoStage(anchor, UCT_STAGE_HTEXT));
1162
1163 /*
1164 * Check to see if our underline and star_string need initialization
1165 * if the underline is not filled with dots.
1166 */
1167 if (underscore_string[0] != '.') {
1168 /*
1169 * Create an array of dots for the UNDERSCORES macro. -FM
1170 */
1171 memset(underscore_string, '.', (size_t) (MAX_LINE - 1));
1172 underscore_string[(MAX_LINE - 1)] = '\0';
1173 underscore_string[MAX_LINE] = '\0';
1174 /*
1175 * Create an array of underscores for the STARS macro. -FM
1176 */
1177 memset(star_string, '_', (size_t) (MAX_LINE - 1));
1178 star_string[(MAX_LINE - 1)] = '\0';
1179 star_string[MAX_LINE] = '\0';
1180 }
1181
1182 underline_on = FALSE; /* reset */
1183 bold_on = FALSE;
1184
1185 #ifdef DISP_PARTIAL
1186 /*
1187 * By this function we create HText object
1188 * so we may start displaying the document while downloading. - LP
1189 */
1190 if (display_partial_flag) {
1191 display_partial = TRUE; /* enable HTDisplayPartial() */
1192 NumOfLines_partial = 0; /* initialize */
1193 }
1194
1195 /*
1196 * These two fields should only be set to valid line numbers
1197 * by calls of display_page during partial displaying. This
1198 * is just so that the FIRST display_page AFTER that can avoid
1199 * repainting the same lines on the screen. - kw
1200 */
1201 ResetPartialLinenos(self);
1202 #endif
1203
1204 #ifdef USE_JUSTIFY_ELTS
1205 ht_justify_cleanup();
1206 #endif
1207 return self;
1208 }
1209
1210 /* Creation Method 2
1211 * ---------------
1212 *
1213 * Stream is assumed open and left open.
1214 */
HText_new2(HTParentAnchor * anchor,HTStream * stream)1215 HText *HText_new2(HTParentAnchor *anchor,
1216 HTStream *stream)
1217 {
1218 HText *result = HText_new(anchor);
1219
1220 if (stream) {
1221 result->target = stream;
1222 result->targetClass = *stream->isa; /* copy action procedures */
1223 }
1224 return result;
1225 }
1226
1227 /* Free Entire Text
1228 * ----------------
1229 */
HText_free(HText * self)1230 void HText_free(HText *self)
1231 {
1232 if (!self)
1233 return;
1234
1235 #if HTLINE_NOT_IN_POOL
1236 {
1237 HTLine *f = FirstHTLine(self);
1238 HTLine *l = self->last_line;
1239
1240 while (l != f) { /* Free off line array */
1241 self->last_line = l->prev;
1242 freeHTLine(self, l);
1243 l = self->last_line;
1244 }
1245 freeHTLine(self, f);
1246 }
1247 #endif
1248
1249 while (self->first_anchor) { /* Free off anchor array */
1250 TextAnchor *l = self->first_anchor;
1251
1252 self->first_anchor = l->next;
1253
1254 if (l->link_type == INPUT_ANCHOR && l->input_field) {
1255 free_form_fields(l->input_field);
1256 }
1257
1258 LYFreeHiText(l);
1259 }
1260 FormList_delete(self->forms);
1261
1262 /*
1263 * Free the tabs list. -FM
1264 */
1265 if (self->tabs) {
1266 HTTabID *Tab = NULL;
1267 HTList *cur = self->tabs;
1268
1269 while (NULL != (Tab = (HTTabID *) HTList_nextObject(cur))) {
1270 FREE(Tab->name);
1271 FREE(Tab);
1272 }
1273 HTList_delete(self->tabs);
1274 self->tabs = NULL;
1275 }
1276
1277 /*
1278 * Free the hidden links list. -FM
1279 */
1280 if (self->hidden_links) {
1281 LYFreeStringList(self->hidden_links);
1282 self->hidden_links = NULL;
1283 }
1284
1285 /*
1286 * Invoke HTAnchor_delete() to free the node_anchor
1287 * if it is not a destination of other links. -FM
1288 */
1289 if (self->node_anchor) {
1290 HTAnchor_resetUCInfoStage(self->node_anchor, -1, UCT_STAGE_STRUCTURED,
1291 UCT_SETBY_NONE);
1292 HTAnchor_resetUCInfoStage(self->node_anchor, -1, UCT_STAGE_HTEXT,
1293 UCT_SETBY_NONE);
1294 #ifdef USE_SOURCE_CACHE
1295 /* Remove source cache files and chunks always, even if the
1296 * HTAnchor_delete call does not actually remove the anchor.
1297 * Keeping them would just be a waste of space - they won't
1298 * be used any more after the anchor has been disassociated
1299 * from a HText structure. - kw
1300 */
1301 HTAnchor_clearSourceCache(self->node_anchor);
1302 #endif
1303
1304 HTAnchor_delete_links(self->node_anchor);
1305
1306 HTAnchor_setDocument(self->node_anchor, (HyperDoc *) 0);
1307
1308 if (HTAnchor_delete(self->node_anchor->parent))
1309 /*
1310 * Make sure HTMainAnchor won't point
1311 * to an invalid structure. - KW
1312 */
1313 HTMainAnchor = NULL;
1314 }
1315
1316 POOL_FREE(self->pool);
1317 FREE(self);
1318 }
1319
1320 /* Display Methods
1321 * ---------------
1322 */
1323
1324 /* Output a line
1325 * -------------
1326 */
display_line(HTLine * line,HText * text,int scrline GCC_UNUSED,const char * target GCC_UNUSED)1327 static int display_line(HTLine *line,
1328 HText *text,
1329 int scrline GCC_UNUSED,
1330 const char *target GCC_UNUSED)
1331 {
1332 register int i, j;
1333 char buffer[7];
1334 char *data;
1335 size_t utf_extra = 0;
1336 char LastDisplayChar = ' ';
1337
1338 #ifdef USE_COLOR_STYLE
1339 int current_style = 0;
1340
1341 #define inunderline NO
1342 #define inbold NO
1343 #else
1344 BOOL inbold = NO, inunderline = NO;
1345 #endif
1346 #if defined(SHOW_WHEREIS_TARGETS) && !defined(USE_COLOR_STYLE)
1347 const char *cp_tgt;
1348 int i_start_tgt = 0, i_after_tgt;
1349 int HitOffset, LenNeeded;
1350 BOOL intarget = NO;
1351
1352 #else
1353 #define intarget NO
1354 #endif /* SHOW_WHEREIS_TARGETS && !USE_COLOR_STYLE */
1355
1356 #if !(defined(NCURSES_VERSION) || defined(WIDEC_CURSES))
1357 text->has_utf8 = NO; /* use as per-line flag, except with ncurses */
1358 #endif
1359
1360 #if defined(WIDEC_CURSES)
1361 /*
1362 * FIXME: this should not be necessary, but in some wide-character pages
1363 * the output line wraps, foiling our attempt to just use newlines to
1364 * advance to the next page.
1365 */
1366 LYmove(scrline + TITLE_LINES - 1, 0);
1367 #endif
1368
1369 /*
1370 * Set up the multibyte character buffer,
1371 * and clear the line to which we will be
1372 * writing.
1373 */
1374 buffer[0] = buffer[1] = buffer[2] = '\0';
1375 LYclrtoeol();
1376
1377 /*
1378 * Add offset, making sure that we do not
1379 * go over the COLS limit on the display.
1380 */
1381 j = (int) line->offset;
1382 if (j >= DISPLAY_COLS)
1383 j = DISPLAY_COLS - 1;
1384 #ifdef USE_SLANG
1385 SLsmg_forward(j);
1386 i = j;
1387 #else
1388 #ifdef USE_COLOR_STYLE
1389 if (line->size == 0)
1390 i = j;
1391 else
1392 #endif
1393 for (i = 0; i < j; i++)
1394 LYaddch(' ');
1395 #endif /* USE_SLANG */
1396
1397 /*
1398 * Add the data, making sure that we do not
1399 * go over the COLS limit on the display.
1400 */
1401 data = line->data;
1402 i++;
1403
1404 #ifndef USE_COLOR_STYLE
1405 #if defined(SHOW_WHEREIS_TARGETS)
1406 /*
1407 * If the target is on this line, it will be emphasized.
1408 */
1409 i_after_tgt = i;
1410 if (target) {
1411 cp_tgt = LYno_attr_mb_strstr(data,
1412 target,
1413 text->T.output_utf8, YES,
1414 &HitOffset,
1415 &LenNeeded);
1416 if (cp_tgt) {
1417 if (((int) line->offset + LenNeeded) >= DISPLAY_COLS) {
1418 cp_tgt = NULL;
1419 } else {
1420 text->page_has_target = YES;
1421 i_start_tgt = i + HitOffset;
1422 i_after_tgt = i + LenNeeded;
1423 }
1424 }
1425 } else {
1426 cp_tgt = NULL;
1427 }
1428 #endif /* SHOW_WHEREIS_TARGETS */
1429 #endif /* USE_COLOR_STYLE */
1430
1431 while ((i <= DISPLAY_COLS) && ((buffer[0] = *data) != '\0')) {
1432
1433 #ifndef USE_COLOR_STYLE
1434 #if defined(SHOW_WHEREIS_TARGETS)
1435 if (cp_tgt && i >= i_after_tgt) {
1436 if (intarget) {
1437 cp_tgt = LYno_attr_mb_strstr(data,
1438 target,
1439 text->T.output_utf8, YES,
1440 &HitOffset,
1441 &LenNeeded);
1442 if (cp_tgt) {
1443 i_start_tgt = i + HitOffset;
1444 i_after_tgt = i + LenNeeded;
1445 }
1446 if (!cp_tgt || i_start_tgt != i) {
1447 LYstopTargetEmphasis();
1448 intarget = NO;
1449 if (inbold)
1450 lynx_start_bold();
1451 if (inunderline)
1452 lynx_start_underline();
1453 }
1454 }
1455 }
1456 #endif /* SHOW_WHEREIS_TARGETS */
1457 #endif /* USE_COLOR_STYLE */
1458
1459 data++;
1460
1461 #if defined(USE_COLOR_STYLE)
1462 #define CStyle line->styles[current_style]
1463
1464 while (current_style < line->numstyles &&
1465 i >= (int) (CStyle.sc_horizpos + line->offset + 1)) {
1466 LynxChangeStyle(CStyle.sc_style, CStyle.sc_direction);
1467 current_style++;
1468 }
1469 #endif
1470 switch (buffer[0]) {
1471
1472 #ifndef USE_COLOR_STYLE
1473 case LY_UNDERLINE_START_CHAR:
1474 if (dump_output_immediately && use_underscore) {
1475 LYaddch('_');
1476 i++;
1477 } else {
1478 inunderline = YES;
1479 if (!intarget) {
1480 #if defined(PDCURSES)
1481 if (LYShowColor == SHOW_COLOR_NEVER)
1482 lynx_start_bold();
1483 else
1484 lynx_start_underline();
1485 #else
1486 lynx_start_underline();
1487 #endif /* PDCURSES */
1488 }
1489 }
1490 break;
1491
1492 case LY_UNDERLINE_END_CHAR:
1493 if (dump_output_immediately && use_underscore) {
1494 LYaddch('_');
1495 i++;
1496 } else {
1497 inunderline = NO;
1498 if (!intarget) {
1499 #if defined(PDCURSES)
1500 if (LYShowColor == SHOW_COLOR_NEVER)
1501 lynx_stop_bold();
1502 else
1503 lynx_stop_underline();
1504 #else
1505 lynx_stop_underline();
1506 #endif /* PDCURSES */
1507 }
1508 }
1509 break;
1510
1511 case LY_BOLD_START_CHAR:
1512 inbold = YES;
1513 if (!intarget)
1514 lynx_start_bold();
1515 break;
1516
1517 case LY_BOLD_END_CHAR:
1518 inbold = NO;
1519 if (!intarget)
1520 lynx_stop_bold();
1521 break;
1522
1523 #endif /* !USE_COLOR_STYLE */
1524 case LY_SOFT_NEWLINE:
1525 if (!dump_output_immediately) {
1526 LYaddch('+');
1527 i++;
1528 #if defined(SHOW_WHEREIS_TARGETS) && !defined(USE_COLOR_STYLE)
1529 i_after_tgt++;
1530 #endif
1531 }
1532 break;
1533
1534 case LY_SOFT_HYPHEN:
1535 if (*data != '\0' ||
1536 isspace(UCH(LastDisplayChar)) ||
1537 LastDisplayChar == '-') {
1538 /*
1539 * Ignore the soft hyphen if it is not the last character in
1540 * the line. Also ignore it if is first character following
1541 * the margin, or if it is preceded by a white character (we
1542 * loaded 'M' into LastDisplayChar if it was a multibyte
1543 * character) or hyphen, though it should have been excluded by
1544 * HText_appendCharacter() or by split_line() in those cases.
1545 * -FM
1546 */
1547 break;
1548 } else {
1549 /*
1550 * Make it a hard hyphen and fall through. -FM
1551 */
1552 buffer[0] = '-';
1553 }
1554 /* FALLTHRU */
1555
1556 default:
1557 #ifndef USE_COLOR_STYLE
1558 #if defined(SHOW_WHEREIS_TARGETS)
1559 if (!intarget && cp_tgt && i >= i_start_tgt) {
1560 /*
1561 * Start the emphasis.
1562 */
1563 if (data > cp_tgt) {
1564 LYstartTargetEmphasis();
1565 intarget = YES;
1566 }
1567 }
1568 #endif /* SHOW_WHEREIS_TARGETS */
1569 #endif /* USE_COLOR_STYLE */
1570 if (text->T.output_utf8 && is8bits(buffer[0])) {
1571 text->has_utf8 = YES;
1572 utf_extra = utf8_length(text->T.output_utf8, data - 1);
1573 LastDisplayChar = 'M';
1574 }
1575 if (utf_extra) {
1576 LYStrNCpy(&buffer[1], data, utf_extra);
1577 LYaddstr(buffer);
1578 buffer[1] = '\0';
1579 data += utf_extra;
1580 utf_extra = 0;
1581 } else if (is_CJK2(buffer[0])) {
1582 /*
1583 * For CJK strings, by Masanobu Kimura.
1584 */
1585 if (i <= DISPLAY_COLS) {
1586 buffer[1] = *data;
1587 buffer[2] = '\0';
1588 data++;
1589 i++;
1590 LYaddstr(buffer);
1591 buffer[1] = '\0';
1592 /*
1593 * For now, load 'M' into LastDisplayChar, but we should
1594 * check whether it's white and if so, use ' '. I don't
1595 * know if there actually are white CJK characters, and
1596 * we're loading ' ' for multibyte spacing characters in
1597 * this code set, but this will become an issue when the
1598 * development code set's multibyte character handling is
1599 * used. -FM
1600 */
1601 LastDisplayChar = 'M';
1602 #ifndef USE_SLANG
1603 {
1604 int y, x;
1605
1606 getyx(LYwin, y, x);
1607 (void) y;
1608 if (x >= DISPLAY_COLS || x == 0)
1609 break;
1610 }
1611 #endif
1612 }
1613 } else {
1614 LYaddstr(buffer);
1615 LastDisplayChar = buffer[0];
1616 }
1617 i++;
1618 } /* end of switch */
1619 } /* end of while */
1620
1621 #if !(defined(NCURSES_VERSION) || defined(WIDEC_CURSES))
1622 if (text->has_utf8) {
1623 LYtouchline(scrline);
1624 text->has_utf8 = NO; /* we had some, but have dealt with it. */
1625 }
1626 #endif
1627 /*
1628 * Add the return.
1629 */
1630 LYaddch('\n');
1631
1632 #if defined(SHOW_WHEREIS_TARGETS) && !defined(USE_COLOR_STYLE)
1633 if (intarget)
1634 LYstopTargetEmphasis();
1635 #else
1636 #undef intarget
1637 #endif /* SHOW_WHEREIS_TARGETS && !USE_COLOR_STYLE */
1638 #ifndef USE_COLOR_STYLE
1639 lynx_stop_underline();
1640 lynx_stop_bold();
1641 #else
1642 while (current_style < line->numstyles) {
1643 LynxChangeStyle(CStyle.sc_style, CStyle.sc_direction);
1644 current_style++;
1645 }
1646 #undef CStyle
1647 #endif
1648 return (0);
1649 }
1650
1651 /* Output the title line
1652 * ---------------------
1653 */
display_title(HText * text)1654 static void display_title(HText *text)
1655 {
1656 char *title = NULL;
1657 char percent[40];
1658 unsigned char *tmp = NULL;
1659 int i = 0, j = 0;
1660 int limit;
1661
1662 #ifdef USE_COLOR_STYLE
1663 int toolbar = 0;
1664 #endif
1665
1666 /*
1667 * Make sure we have a text structure. -FM
1668 */
1669 if (!text)
1670 return;
1671
1672 lynx_start_title_color();
1673 #ifdef USE_COLOR_STYLE
1674 /* turn the TITLE style on */
1675 if (last_colorattr_ptr > 0) {
1676 LynxChangeStyle(s_title, STACK_ON);
1677 } else {
1678 LynxChangeStyle(s_title, ABS_ON);
1679 }
1680 #endif /* USE_COLOR_STYLE */
1681
1682 /*
1683 * Load the title field. -FM
1684 */
1685 StrAllocCopy(title,
1686 (HTAnchor_title(text->node_anchor) ?
1687 HTAnchor_title(text->node_anchor) : " ")); /* "" -> " " */
1688 LYReduceBlanks(title);
1689
1690 /*
1691 * Generate the page indicator (percent) string.
1692 */
1693 limit = LYscreenWidth();
1694 if (limit < 10) {
1695 percent[0] = '\0';
1696 } else if ((display_lines) <= 0 && LYlines > 0 &&
1697 text->top_of_screen <= 99999 && text->Lines <= 999999) {
1698 sprintf(percent, " (l%d of %d)",
1699 text->top_of_screen, text->Lines);
1700 } else if ((text->Lines >= display_lines) && (display_lines > 0)) {
1701 int total_pages = ((text->Lines + display_lines)
1702 / display_lines);
1703 int start_of_last_page = ((text->Lines <= display_lines)
1704 ? 0
1705 : (text->Lines - display_lines));
1706
1707 sprintf(percent, " (p%d of %d)",
1708 ((text->top_of_screen > start_of_last_page)
1709 ? total_pages
1710 : ((text->top_of_screen + display_lines) / (display_lines))),
1711 total_pages);
1712 } else {
1713 percent[0] = '\0';
1714 }
1715
1716 /*
1717 * Generate and display the title string, with page indicator
1718 * if appropriate, preceded by the toolbar token if appropriate,
1719 * and truncated if necessary. -FM & KW
1720 */
1721 if (IS_CJK_TTY) {
1722 if (*title &&
1723 (tmp = typecallocn(unsigned char, (strlen(title) * 2 + 256)))) {
1724 if (kanji_code == EUC) {
1725 TO_EUC((unsigned char *) title, tmp);
1726 } else if (kanji_code == SJIS) {
1727 TO_SJIS((unsigned char *) title, tmp);
1728 } else {
1729 for (i = 0, j = 0; title[i]; i++) {
1730 if (title[i] != CH_ESC) { /* S/390 -- gil -- 1487 */
1731 tmp[j++] = UCH(title[i]);
1732 }
1733 }
1734 tmp[j] = '\0';
1735 }
1736 StrAllocCopy(title, (const char *) tmp);
1737 FREE(tmp);
1738 }
1739 }
1740 LYmove(0, 0);
1741 LYclrtoeol();
1742 #if defined(SH_EX) && defined(KANJI_CODE_OVERRIDE)
1743 LYaddstr(str_kcode(last_kcode));
1744 #endif
1745 if (HText_hasToolbar(text)) {
1746 LYaddch('#');
1747 #ifdef USE_COLOR_STYLE
1748 toolbar = 1;
1749 #endif
1750 }
1751 #ifdef USE_COLOR_STYLE
1752 if (s_forw_backw != NOSTYLE && (nhist || nhist_extra > 1)) {
1753 chtype c = nhist ? ACS_LARROW : ' ';
1754
1755 /* turn the FORWBACKW.ARROW style on */
1756 LynxChangeStyle(s_forw_backw, STACK_ON);
1757 if (nhist) {
1758 LYaddch(c);
1759 LYaddch(c);
1760 LYaddch(c);
1761 } else
1762 LYmove(0, 3 + toolbar);
1763 if (nhist_extra > 1) {
1764 LYaddch(ACS_RARROW);
1765 LYaddch(ACS_RARROW);
1766 LYaddch(ACS_RARROW);
1767 }
1768 LynxChangeStyle(s_forw_backw, STACK_OFF);
1769 }
1770 #endif /* USE_COLOR_STYLE */
1771 #ifdef WIDEC_CURSES
1772 i = limit - LYbarWidth - (int) strlen(percent) - LYstrCells(title);
1773 if (i <= 0) { /* title is truncated */
1774 i = limit - LYbarWidth - (int) strlen(percent) - 3;
1775 if (i <= 0) { /* no room at all */
1776 title[0] = '\0';
1777 } else {
1778 strcpy(title + LYstrFittable(title, i), "...");
1779 }
1780 i = 0;
1781 }
1782 LYmove(0, i);
1783 #else
1784 i = (limit - 1) - (int) (strlen(percent) + strlen(title));
1785 if (i >= CHAR_WIDTH) {
1786 LYmove(0, i);
1787 } else {
1788 /*
1789 * Truncation takes into account the possibility that
1790 * multibyte characters might be present. -HS (H. Senshu)
1791 */
1792 int last;
1793
1794 last = (int) strlen(percent) + CHAR_WIDTH;
1795 if (limit - 3 >= last) {
1796 title[(limit - 3) - last] = '.';
1797 title[(limit - 2) - last] = '.';
1798 title[(limit - 1) - last] = '\0';
1799 } else {
1800 title[(limit - 1) - last] = '\0';
1801 }
1802 LYmove(0, CHAR_WIDTH);
1803 }
1804 #endif
1805 LYaddstr(title);
1806 if (percent[0] != '\0')
1807 LYaddstr(percent);
1808 LYaddch('\n');
1809 FREE(title);
1810
1811 #if defined(USE_COLOR_STYLE) && defined(CAN_CUT_AND_PASTE)
1812 if (s_hot_paste != NOSTYLE) { /* Only if the user set the style */
1813 LYmove(0, LYcolLimit);
1814 LynxChangeStyle(s_hot_paste, STACK_ON);
1815 LYaddch(ACS_RARROW);
1816 LynxChangeStyle(s_hot_paste, STACK_OFF);
1817 LYmove(1, 0); /* As after \n */
1818 }
1819 #endif /* USE_COLOR_STYLE */
1820
1821 #ifdef USE_COLOR_STYLE
1822 /* turn the TITLE style off */
1823 LynxChangeStyle(s_title, STACK_OFF);
1824 #endif /* USE_COLOR_STYLE */
1825 lynx_stop_title_color();
1826
1827 return;
1828 }
1829
1830 /* Output the scrollbar
1831 * ---------------------
1832 */
1833 #ifdef USE_SCROLLBAR
display_scrollbar(HText * text)1834 static void display_scrollbar(HText *text)
1835 {
1836 int i;
1837 int h = display_lines - 2 * (LYsb_arrow != 0); /* Height of the scrollbar */
1838 int off = (LYsb_arrow != 0); /* Start of the scrollbar */
1839 int top_skip, bot_skip, sh, shown;
1840
1841 LYsb_begin = LYsb_end = -1;
1842 if (!LYShowScrollbar || !text || h <= 2
1843 || text->Lines <= display_lines)
1844 return;
1845
1846 if (text->top_of_screen >= text->Lines - display_lines) {
1847 /* Only part of the screen shows actual text */
1848 shown = text->Lines - text->top_of_screen;
1849
1850 if (shown <= 0)
1851 shown = 1;
1852 } else
1853 shown = display_lines;
1854 /* Each cell of scrollbar represents text->Lines/h lines of text. */
1855 /* Always smaller than h */
1856 sh = (shown * h + text->Lines / 2) / text->Lines;
1857 if (sh <= 0)
1858 sh = 1;
1859 if (sh >= h - 1)
1860 sh = h - 2; /* Position at ends indicates BEG and END */
1861
1862 if (text->top_of_screen == 0)
1863 top_skip = 0;
1864 else if (text->Lines - (text->top_of_screen + display_lines - 1) <= 0)
1865 top_skip = h - sh;
1866 else {
1867 /* text->top_of_screen between 1 and text->Lines - display_lines
1868 corresponds to top_skip between 1 and h - sh - 1 */
1869 /* Use rounding to get as many positions into top_skip==h - sh - 1
1870 as into top_skip == 1:
1871 1--->1, text->Lines - display_lines + 1--->h - sh. */
1872 top_skip = (int) (1 +
1873 1. * (h - sh - 1) * text->top_of_screen
1874 / (text->Lines - display_lines + 1));
1875 }
1876 bot_skip = h - sh - top_skip;
1877
1878 LYsb_begin = top_skip;
1879 LYsb_end = h - bot_skip;
1880
1881 if (LYsb_arrow) {
1882 #ifdef USE_COLOR_STYLE
1883 int s = top_skip ? s_sb_aa : s_sb_naa;
1884
1885 if (last_colorattr_ptr > 0) {
1886 LynxChangeStyle(s, STACK_ON);
1887 } else {
1888 LynxChangeStyle(s, ABS_ON);
1889 }
1890 #endif /* USE_COLOR_STYLE */
1891 LYmove(1, LYcolLimit + LYshiftWin);
1892 addch_raw(ACS_UARROW);
1893 #ifdef USE_COLOR_STYLE
1894 LynxChangeStyle(s, STACK_OFF);
1895 #endif /* USE_COLOR_STYLE */
1896 }
1897 #ifdef USE_COLOR_STYLE
1898 if (last_colorattr_ptr > 0) {
1899 LynxChangeStyle(s_sb_bg, STACK_ON);
1900 } else {
1901 LynxChangeStyle(s_sb_bg, ABS_ON);
1902 }
1903 #endif /* USE_COLOR_STYLE */
1904
1905 for (i = 1; i <= h; i++) {
1906 #ifdef USE_COLOR_STYLE
1907 if (i - 1 <= top_skip && i > top_skip)
1908 LynxChangeStyle(s_sb_bar, STACK_ON);
1909 if (i - 1 <= h - bot_skip && i > h - bot_skip)
1910 LynxChangeStyle(s_sb_bar, STACK_OFF);
1911 #endif /* USE_COLOR_STYLE */
1912 LYmove(i + off, LYcolLimit + LYshiftWin);
1913 if (i > top_skip && i <= h - bot_skip) {
1914 LYaddch(ACS_BLOCK);
1915 } else {
1916 LYaddch(ACS_CKBOARD);
1917 }
1918 }
1919 #ifdef USE_COLOR_STYLE
1920 LynxChangeStyle(s_sb_bg, STACK_OFF);
1921 #endif /* USE_COLOR_STYLE */
1922
1923 if (LYsb_arrow) {
1924 #ifdef USE_COLOR_STYLE
1925 int s = bot_skip ? s_sb_aa : s_sb_naa;
1926
1927 if (last_colorattr_ptr > 0) {
1928 LynxChangeStyle(s, STACK_ON);
1929 } else {
1930 LynxChangeStyle(s, ABS_ON);
1931 }
1932 #endif /* USE_COLOR_STYLE */
1933 LYmove(h + 2, LYcolLimit + LYshiftWin);
1934 addch_raw(ACS_DARROW);
1935 #ifdef USE_COLOR_STYLE
1936 LynxChangeStyle(s, STACK_OFF);
1937 #endif /* USE_COLOR_STYLE */
1938 }
1939 return;
1940 }
1941 #else
1942 #define display_scrollbar(text) /*nothing */
1943 #endif /* USE_SCROLLBAR */
1944
1945 /* Output a page
1946 * -------------
1947 */
display_page(HText * text,int line_number,const char * target)1948 static void display_page(HText *text,
1949 int line_number,
1950 const char *target)
1951 {
1952 HTLine *line = NULL;
1953 int i;
1954 int title_lines = TITLE_LINES;
1955
1956 #if defined(USE_COLOR_STYLE) && defined(SHOW_WHEREIS_TARGETS)
1957 const char *cp;
1958 #endif
1959 char tmp[7];
1960 TextAnchor *Anchor_ptr = NULL;
1961 int stop_before_for_anchors;
1962 FormInfo *FormInfo_ptr;
1963 BOOL display_flag = FALSE;
1964 HTAnchor *link_dest;
1965 HTAnchor *link_dest_intl = NULL;
1966 static int last_nlinks = 0;
1967 static int charset_last_displayed = -1;
1968
1969 #ifdef DISP_PARTIAL
1970 int last_disp_partial = -1;
1971 #endif
1972
1973 lynx_mode = NORMAL_LYNX_MODE;
1974
1975 if (text == NULL) {
1976 /*
1977 * Check whether to force a screen clear to enable scrollback,
1978 * or as a hack to fix a reverse clear screen problem for some
1979 * curses packages. - shf@access.digex.net & seldon@eskimo.com
1980 */
1981 if (enable_scrollback) {
1982 LYaddch('*');
1983 LYrefresh();
1984 LYclear();
1985 }
1986 LYaddstr("\n\nError accessing document!\nNo data available!\n");
1987 LYrefresh();
1988 nlinks = 0; /* set number of links to 0 */
1989 return;
1990 }
1991 #ifdef DISP_PARTIAL
1992 if (display_partial || recent_sizechange || text->stale) {
1993 /* Reset them, will be set near end if all is okay. - kw */
1994 ResetPartialLinenos(text);
1995 }
1996 #endif /* DISP_PARTIAL */
1997
1998 tmp[0] = tmp[1] = tmp[2] = '\0';
1999 if (target && *target == '\0')
2000 target = NULL;
2001 text->page_has_target = NO;
2002 if (display_lines <= 0) {
2003 /* No screen space to display anything!
2004 * returning here makes it more likely we will survive if
2005 * an xterm is temporarily made very small. - kw */
2006 return;
2007 }
2008
2009 line_number = HText_getPreferredTopLine(text, line_number);
2010
2011 for (i = 0, line = FirstHTLine(text); /* Find line */
2012 i < line_number && (line != text->last_line);
2013 i++, line = line->next) { /* Loop */
2014 #ifndef VMS
2015 if (!LYNoCore) {
2016 assert(line->next != NULL);
2017 } else if (line->next == NULL) {
2018 if (enable_scrollback) {
2019 LYaddch('*');
2020 LYrefresh();
2021 LYclear();
2022 }
2023 LYaddstr("\n\nError drawing page!\nBad HText structure!\n");
2024 LYrefresh();
2025 nlinks = 0; /* set number of links to 0 */
2026 return;
2027 }
2028 #else
2029 assert(line->next != NULL);
2030 #endif /* !VMS */
2031 } /* Loop */
2032
2033 if (LYlowest_eightbit[current_char_set] <= 255 &&
2034 (current_char_set != charset_last_displayed) &&
2035 /*
2036 * current_char_set has changed since last invocation,
2037 * and it's not just 7-bit.
2038 * Also we don't want to do this for -dump and -source etc.
2039 */
2040 LYCursesON) {
2041 #ifdef EXP_CHARTRANS_AUTOSWITCH
2042 UCChangeTerminalCodepage(current_char_set,
2043 &LYCharSet_UC[current_char_set]);
2044 #endif /* EXP_CHARTRANS_AUTOSWITCH */
2045 charset_last_displayed = current_char_set;
2046 }
2047
2048 /*
2049 * Check whether to force a screen clear to enable scrollback,
2050 * or as a hack to fix a reverse clear screen problem for some
2051 * curses packages. - shf@access.digex.net & seldon@eskimo.com
2052 */
2053 if (enable_scrollback) {
2054 LYaddch('*');
2055 LYrefresh();
2056 LYclear();
2057 }
2058 #ifdef USE_COLOR_STYLE
2059 /*
2060 * Reset stack of color attribute changes to avoid color leaking,
2061 * except if what we last displayed from this text was the previous
2062 * screenful, in which case carrying over the state might be beneficial
2063 * (although it shouldn't generally be needed any more). - kw
2064 */
2065 if (text->stale ||
2066 line_number != text->top_of_screen + (display_lines)) {
2067 last_colorattr_ptr = 0;
2068 }
2069 #endif
2070
2071 text->top_of_screen = line_number;
2072 text->top_of_screen_line = line;
2073 if (no_title) {
2074 LYmove(0, 0);
2075 title_lines = 0;
2076 } else {
2077 display_title(text); /* will move cursor to top of screen */
2078 }
2079 display_flag = TRUE;
2080
2081 #ifdef USE_COLOR_STYLE
2082 #ifdef DISP_PARTIAL
2083 if (display_partial ||
2084 line_number != text->first_lineno_last_disp_partial ||
2085 line_number > text->last_lineno_last_disp_partial)
2086 #endif /* DISP_PARTIAL */
2087 ResetCachedStyles();
2088 #endif /* USE_COLOR_STYLE */
2089
2090 #ifdef DISP_PARTIAL
2091 if (display_partial && text->stbl) {
2092 stop_before_for_anchors = Stbl_getStartLineDeep(text->stbl);
2093 if (stop_before_for_anchors > line_number + (display_lines))
2094 stop_before_for_anchors = line_number + (display_lines);
2095 } else
2096 #endif
2097 stop_before_for_anchors = line_number + (display_lines);
2098
2099 /*
2100 * Output the page.
2101 */
2102 if (line) {
2103 #if defined(USE_COLOR_STYLE) && defined(SHOW_WHEREIS_TARGETS)
2104 char *data;
2105 int offset, LenNeeded;
2106 #endif
2107 #ifdef DISP_PARTIAL
2108 if (display_partial ||
2109 line_number != text->first_lineno_last_disp_partial)
2110 text->has_utf8 = NO;
2111 #else
2112 text->has_utf8 = NO;
2113 #endif
2114 for (i = 0; i < (display_lines); i++) {
2115 /*
2116 * Verify and display each line.
2117 */
2118 #ifndef VMS
2119 if (!LYNoCore) {
2120 assert(line != NULL);
2121 } else if (line == NULL) {
2122 if (enable_scrollback) {
2123 LYaddch('*');
2124 LYrefresh();
2125 LYclear();
2126 }
2127 LYaddstr("\n\nError drawing page!\nBad HText structure!\n");
2128 LYrefresh();
2129 nlinks = 0; /* set number of links to 0 */
2130 return;
2131 }
2132 #else
2133 assert(line != NULL);
2134 #endif /* !VMS */
2135
2136 #ifdef DISP_PARTIAL
2137 if (!display_partial &&
2138 line_number == text->first_lineno_last_disp_partial &&
2139 i + line_number <= text->last_lineno_last_disp_partial)
2140 LYmove((i + title_lines + 1), 0);
2141 else
2142 #endif
2143 display_line(line, text, i + 1, target);
2144
2145 #if defined(SHOW_WHEREIS_TARGETS)
2146 #ifdef USE_COLOR_STYLE /* otherwise done in display_line - kw */
2147 /*
2148 * If the target is on this line, recursively
2149 * seek and emphasize it. -FM
2150 */
2151 data = (char *) line->data;
2152 offset = (int) line->offset;
2153 while (non_empty(target) &&
2154 (cp = LYno_attr_mb_strstr(data,
2155 target,
2156 text->T.output_utf8, YES,
2157 NULL,
2158 &LenNeeded)) != NULL &&
2159 ((int) line->offset + LenNeeded) <= DISPLAY_COLS) {
2160 size_t itmp = 0;
2161 size_t written = 0;
2162 int x_off = offset + (int) (cp - data);
2163 size_t len = strlen(target);
2164 size_t utf_extra = 0;
2165
2166 text->page_has_target = YES;
2167
2168 /*
2169 * Start the emphasis.
2170 */
2171 LYstartTargetEmphasis();
2172
2173 /*
2174 * Output the target characters.
2175 */
2176 for (;
2177 written < len && (tmp[0] = data[itmp]) != '\0';
2178 itmp++) {
2179 if (IsSpecialAttrChar(tmp[0]) && tmp[0] != LY_SOFT_NEWLINE) {
2180 /*
2181 * Ignore special characters.
2182 */
2183 x_off--;
2184
2185 } else if (&data[itmp] >= cp) {
2186 if (cp == &data[itmp]) {
2187 /*
2188 * First printable character of target.
2189 */
2190 LYmove((i + title_lines),
2191 line->offset + LYstrExtent2(line->data,
2192 x_off - line->offset));
2193 }
2194 /*
2195 * Output all the printable target chars.
2196 */
2197 utf_extra = utf8_length(text->T.output_utf8, data + itmp);
2198 if (utf_extra) {
2199 LYStrNCpy(&tmp[1], &line->data[itmp + 1], utf_extra);
2200 itmp += utf_extra;
2201 LYaddstr(tmp);
2202 tmp[1] = '\0';
2203 written += (utf_extra + 1);
2204 } else if (IS_CJK_TTY && is8bits(tmp[0])) {
2205 /*
2206 * For CJK strings, by Masanobu Kimura.
2207 */
2208 tmp[1] = data[++itmp];
2209 LYaddstr(tmp);
2210 tmp[1] = '\0';
2211 written += 2;
2212 } else {
2213 LYaddstr(tmp);
2214 written++;
2215 }
2216 }
2217 }
2218
2219 /*
2220 * Stop the emphasis, and reset the offset and
2221 * data pointer for our current position in the
2222 * line. -FM
2223 */
2224 LYstopTargetEmphasis();
2225 data = (char *) &data[itmp];
2226 offset = (int) (data - line->data + line->offset);
2227
2228 } /* end while */
2229 LYmove((i + title_lines + 1), 0);
2230 #endif /* USE_COLOR_STYLE */
2231 #endif /* SHOW_WHEREIS_TARGETS */
2232
2233 /*
2234 * Stop if this is the last line. Otherwise, make sure
2235 * display_flag is set and process the next line. -FM
2236 */
2237 if (line == text->last_line) {
2238 /*
2239 * Clear remaining lines of display.
2240 */
2241 for (i++; i < (display_lines); i++) {
2242 LYmove((i + title_lines), 0);
2243 LYclrtoeol();
2244 }
2245 break;
2246 }
2247 #ifdef DISP_PARTIAL
2248 if (display_partial) {
2249 /*
2250 * Remember as fully shown during last partial display,
2251 * if it was not the last text line. - kw
2252 */
2253 last_disp_partial = i + line_number;
2254 }
2255 #endif /* DISP_PARTIAL */
2256 display_flag = TRUE;
2257 line = line->next;
2258 } /* end of "Verify and display each line." loop */
2259 }
2260 /* end "Output the page." */
2261 text->next_line = line; /* Line after screen */
2262 text->stale = NO; /* Display is up-to-date */
2263
2264 /*
2265 * Add the anchors to Lynx structures.
2266 */
2267 nlinks = 0;
2268 for (Anchor_ptr = text->first_anchor;
2269 Anchor_ptr != NULL && Anchor_ptr->line_num <= stop_before_for_anchors;
2270 Anchor_ptr = Anchor_ptr->next) {
2271
2272 if (Anchor_ptr->line_num >= line_number
2273 && Anchor_ptr->line_num < stop_before_for_anchors) {
2274 char *hi_string = LYGetHiTextStr(Anchor_ptr, 0);
2275
2276 /*
2277 * Load normal hypertext anchors.
2278 */
2279 if (Anchor_ptr->show_anchor
2280 && non_empty(hi_string)
2281 && (Anchor_ptr->link_type & HYPERTEXT_ANCHOR)) {
2282 int count;
2283 char *s;
2284
2285 for (count = 0;; ++count) {
2286 s = LYGetHiTextStr(Anchor_ptr, count);
2287 if (count == 0)
2288 LYSetHilite(nlinks, s);
2289 if (s == NULL)
2290 break;
2291 if (count != 0) {
2292 LYAddHilite(nlinks, s, LYGetHiTextPos(Anchor_ptr, count));
2293 }
2294 }
2295
2296 links[nlinks].inUnderline = Anchor_ptr->inUnderline;
2297
2298 links[nlinks].sgml_offset = Anchor_ptr->sgml_offset;
2299 links[nlinks].anchor_number = Anchor_ptr->number;
2300 links[nlinks].anchor_line_num = Anchor_ptr->line_num;
2301
2302 link_dest = HTAnchor_followLink(Anchor_ptr->anchor);
2303 {
2304 auto char *cp_AnchorAddress = NULL;
2305
2306 if (traversal) {
2307 cp_AnchorAddress = stub_HTAnchor_address(link_dest);
2308 } else if (track_internal_links) {
2309 if (Anchor_ptr->link_type == INTERNAL_LINK_ANCHOR) {
2310 link_dest_intl = HTAnchor_followTypedLink(Anchor_ptr->anchor,
2311 HTInternalLink);
2312 if (link_dest_intl && link_dest_intl != link_dest) {
2313
2314 CTRACE((tfp,
2315 "GridText: display_page: unexpected typed link to %s!\n",
2316 link_dest_intl->parent->address));
2317 link_dest_intl = NULL;
2318 }
2319 } else {
2320 link_dest_intl = NULL;
2321 }
2322 if (link_dest_intl) {
2323 char *cp2 = HTAnchor_address(link_dest_intl);
2324
2325 cp_AnchorAddress = cp2;
2326 } else {
2327 cp_AnchorAddress = HTAnchor_address(link_dest);
2328 }
2329 } else {
2330 cp_AnchorAddress = HTAnchor_address(link_dest);
2331 }
2332 FREE(links[nlinks].lname);
2333
2334 if (cp_AnchorAddress != NULL)
2335 links[nlinks].lname = cp_AnchorAddress;
2336 else
2337 StrAllocCopy(links[nlinks].lname, empty_string);
2338 }
2339
2340 links[nlinks].lx = Anchor_ptr->line_pos;
2341 links[nlinks].ly = ((Anchor_ptr->line_num + 1) - line_number);
2342 if (link_dest_intl)
2343 links[nlinks].type = WWW_INTERN_LINK_TYPE;
2344 else
2345 links[nlinks].type = WWW_LINK_TYPE;
2346 links[nlinks].target = empty_string;
2347 links[nlinks].l_form = NULL;
2348
2349 nlinks++;
2350 display_flag = TRUE;
2351
2352 } else if (Anchor_ptr->link_type == INPUT_ANCHOR
2353 && Anchor_ptr->input_field->type != F_HIDDEN_TYPE) {
2354 /*
2355 * Handle form fields.
2356 */
2357 lynx_mode = FORMS_LYNX_MODE;
2358
2359 FormInfo_ptr = Anchor_ptr->input_field;
2360
2361 links[nlinks].sgml_offset = Anchor_ptr->sgml_offset;
2362 links[nlinks].anchor_number = Anchor_ptr->number;
2363 links[nlinks].anchor_line_num = Anchor_ptr->line_num;
2364
2365 links[nlinks].l_form = FormInfo_ptr;
2366 links[nlinks].lx = Anchor_ptr->line_pos;
2367 links[nlinks].ly = ((Anchor_ptr->line_num + 1) - line_number);
2368 links[nlinks].type = WWW_FORM_LINK_TYPE;
2369 links[nlinks].inUnderline = Anchor_ptr->inUnderline;
2370 links[nlinks].target = empty_string;
2371 StrAllocCopy(links[nlinks].lname, empty_string);
2372
2373 if (FormInfo_ptr->type == F_RADIO_TYPE) {
2374 LYSetHilite(nlinks,
2375 FormInfo_ptr->num_value
2376 ? checked_radio
2377 : unchecked_radio);
2378 } else if (FormInfo_ptr->type == F_CHECKBOX_TYPE) {
2379 LYSetHilite(nlinks,
2380 FormInfo_ptr->num_value
2381 ? checked_box
2382 : unchecked_box);
2383 } else if (FormInfo_ptr->type == F_PASSWORD_TYPE) {
2384 LYSetHilite(nlinks,
2385 STARS(LYstrCells(FormInfo_ptr->value)));
2386 } else { /* TEXT type */
2387 LYSetHilite(nlinks,
2388 FormInfo_ptr->value);
2389 }
2390
2391 nlinks++;
2392 /*
2393 * Bold the link after incrementing nlinks.
2394 */
2395 LYhighlight(FALSE, (nlinks - 1), target);
2396
2397 display_flag = TRUE;
2398
2399 } else {
2400 /*
2401 * Not showing anchor.
2402 */
2403 if (non_empty(hi_string))
2404 CTRACE((tfp,
2405 "\nGridText: Not showing link, hightext=%s\n",
2406 hi_string));
2407 }
2408 }
2409
2410 if (nlinks == MAXLINKS) {
2411 /*
2412 * Links array is full. If interactive, tell user
2413 * to use half-page or two-line scrolling. -FM
2414 */
2415 if (LYCursesON) {
2416 HTAlert(MAXLINKS_REACHED);
2417 }
2418 CTRACE((tfp, "\ndisplay_page: MAXLINKS reached.\n"));
2419 break;
2420 }
2421 } /* end of loop "Add the anchors to Lynx structures." */
2422
2423 /*
2424 * Free any un-reallocated links[] entries
2425 * from the previous page draw. -FM
2426 */
2427 LYFreeHilites(nlinks, last_nlinks);
2428 last_nlinks = nlinks;
2429
2430 /*
2431 * If Anchor_ptr is not NULL and is not pointing to the last
2432 * anchor, then there are anchors farther down in the document,
2433 * and we need to flag this for traversals.
2434 */
2435 more_links = FALSE;
2436 if (traversal && Anchor_ptr) {
2437 if (Anchor_ptr->next)
2438 more_links = TRUE;
2439 }
2440
2441 if (!display_flag) {
2442 /*
2443 * Nothing on the page.
2444 */
2445 LYaddstr("\n Document is empty");
2446 }
2447 display_scrollbar(text);
2448
2449 #ifdef DISP_PARTIAL
2450 if (display_partial && display_flag &&
2451 last_disp_partial >= text->top_of_screen &&
2452 !enable_scrollback &&
2453 !recent_sizechange) { /* really remember them if ok - kw */
2454 text->first_lineno_last_disp_partial = text->top_of_screen;
2455 text->last_lineno_last_disp_partial = last_disp_partial;
2456 } else {
2457 ResetPartialLinenos(text);
2458 }
2459 #endif /* DISP_PARTIAL */
2460
2461 #if !defined(WIDEC_CURSES)
2462 if (text->has_utf8 || text->had_utf8) {
2463 /*
2464 * For other than ncurses, repainting is taken care of
2465 * by touching lines in display_line and highlight. - kw 1999-10-07
2466 */
2467 text->had_utf8 = text->has_utf8;
2468 clearok(curscr, TRUE);
2469 } else if (IS_CJK_TTY) {
2470 /*
2471 * For non-multibyte curses.
2472 *
2473 * Full repainting is necessary, otherwise only part of a multibyte
2474 * character sequence might be written because of curses output
2475 * optimizations.
2476 */
2477 clearok(curscr, TRUE);
2478 }
2479 #endif /* WIDEC_CURSES */
2480
2481 LYrefresh();
2482 return;
2483 }
2484
2485 /* Object Building methods
2486 * -----------------------
2487 *
2488 * These are used by a parser to build the text in an object
2489 */
HText_beginAppend(HText * text)2490 void HText_beginAppend(HText *text)
2491 {
2492 text->permissible_split = 0;
2493 text->in_line_1 = YES;
2494
2495 }
2496
2497 /*
2498 * LYcols_cu is the notion that the display library has of the screen width.
2499 * Checks of the line length (as the non-UTF-8-aware display library would see
2500 * it) against LYcols_cu are used to try to prevent lines with UTF-8 chars from
2501 * being wrapped by the library when they shouldn't. If there is no display
2502 * library involved, i.e., dump_output_immediately, no such limit should be
2503 * imposed. MAX_COLS should be just as good as any other large value. (But
2504 * don't use INT_MAX or something close to it to, avoid over/underflow.) - kw
2505 */
2506 #ifdef USE_SLANG
2507 #define LYcols_cu(text) (dump_output_immediately ? MAX_COLS : SLtt_Screen_Cols)
2508 #else
2509 #ifdef WIDEC_CURSES
2510 #define LYcols_cu(text) WRAP_COLS(text)
2511 #else
2512 #define LYcols_cu(text) (dump_output_immediately ? MAX_COLS : DISPLAY_COLS)
2513 #endif
2514 #endif
2515
2516 /* Add a new line of text
2517 * ----------------------
2518 *
2519 * On entry,
2520 *
2521 * split is zero for newline function, else number of characters
2522 * before split.
2523 * text->display_on_the_fly
2524 * may be set to indicate direct output of the finished line.
2525 * On exit,
2526 * A new line has been made, justified according to the
2527 * current style. Text after the split (if split nonzero)
2528 * is taken over onto the next line.
2529 *
2530 * If display_on_the_fly is set, then it is decremented and
2531 * the finished line is displayed.
2532 */
2533
set_style_by_embedded_chars(char * s,char * e,unsigned start_c,unsigned end_c)2534 static int set_style_by_embedded_chars(char *s,
2535 char *e,
2536 unsigned start_c,
2537 unsigned end_c)
2538 {
2539 int ret = NO;
2540
2541 while (--e >= s) {
2542 if (UCH(*e) == UCH(end_c))
2543 break;
2544 if (UCH(*e) == UCH(start_c)) {
2545 ret = YES;
2546 break;
2547 }
2548 }
2549 return ret;
2550 }
2551
move_anchors_in_region(HTLine * line,int line_number,TextAnchor ** prev_anchor,int * prev_head_processed,int sbyte,int ebyte,int shift)2552 static void move_anchors_in_region(HTLine *line, int line_number,
2553 TextAnchor **prev_anchor, /*updates++ */
2554 int *prev_head_processed,
2555 int sbyte,
2556 int ebyte,
2557 int shift) /* Likewise */
2558 {
2559 /*
2560 * Update anchor positions for anchors that start on this line. Note: we
2561 * rely on a->line_pos counting bytes, not characters. That's one reason
2562 * why HText_trimHightext has to be prevented from acting on these anchors
2563 * in partial display mode before we get a chance to deal with them here.
2564 */
2565 TextAnchor *a;
2566 int head_processed = *prev_head_processed;
2567
2568 /*
2569 * We need to know whether (*prev_anchor)->line_pos is "in new coordinates"
2570 * or in old ones. If prev_anchor' head was touched on the previous
2571 * iteration, we set head_processed. The tail may need to be treated now.
2572 */
2573 for (a = *prev_anchor;
2574 a && a->line_num <= line_number;
2575 a = a->next, head_processed = 0) {
2576 /* extent==0 needs to be special-cased; happens if no text for
2577 the anchor was processed yet. */
2578 /* Subtract one so that the space is not inserted at the end
2579 of the anchor... */
2580 int last = a->line_pos + (a->extent ? a->extent - 1 : 0);
2581
2582 /* Include the anchors started on the previous line */
2583 if (a->line_num < line_number - 1)
2584 continue;
2585 if (a->line_num == line_number - 1)
2586 last -= line->prev->size + 1; /* Fake "\n" "between" lines counted too */
2587 if (last < sbyte) /* Completely before the start */
2588 continue;
2589
2590 if (!head_processed /* a->line_pos is not edited yet */
2591 && a->line_num == line_number
2592 && a->line_pos >= ebyte) /* Completely after the end */
2593 break;
2594 /* Now we know that the anchor context intersects the chunk */
2595
2596 /* Fix the start */
2597 if (!head_processed && a->line_num == line_number
2598 && a->line_pos >= sbyte) {
2599 a->line_pos = (short) (a->line_pos + shift);
2600 a->extent = (short) (a->extent - shift);
2601 head_processed = 1;
2602 }
2603 /* Fix the end */
2604 if (last < ebyte) {
2605 a->extent = (short) (a->extent + shift);
2606 } else {
2607 break; /* Keep this `a' for the next step */
2608 }
2609 }
2610 *prev_anchor = a;
2611 *prev_head_processed = head_processed;
2612 }
2613
2614 /*
2615 * Given a line and two int arrays of old/now position, this function
2616 * creates a new line where spaces have been inserted/removed
2617 * in appropriate places - so that characters at/after the old
2618 * position end up at/after the new position, for each pair, if possible.
2619 * Some necessary changes for anchors starting on this line are also done
2620 * here if needed. Updates 'prev_anchor' internally.
2621 * Returns a newly allocated HTLine* if changes were made
2622 * (caller has to free the old one).
2623 * Returns NULL if no changes needed. (Remove-spaces code may be buggy...)
2624 * - kw
2625 */
insert_blanks_in_line(HTLine * line,int line_number,HText * text,TextAnchor ** prev_anchor,int ninserts,int * oldpos,int * newpos)2626 static HTLine *insert_blanks_in_line(HTLine *line, int line_number,
2627 HText *text,
2628 TextAnchor **prev_anchor, /*updates++ */
2629 int ninserts,
2630 int *oldpos, /* Measured in cells */
2631 int *newpos) /* Likewise */
2632 {
2633 int ioldc = 0; /* count visible characters */
2634 int ip; /* count insertion pairs */
2635
2636 #if defined(USE_COLOR_STYLE)
2637 int istyle = 0;
2638 #endif
2639 int added_chars = 0;
2640 int shift = 0;
2641 int head_processed;
2642 HTLine *mod_line;
2643 char *newdata;
2644 char *s = line->data;
2645 char *pre = s;
2646 char *copied = line->data, *t;
2647
2648 if (!(line && line->size && ninserts))
2649 return NULL;
2650 for (ip = 0; ip < ninserts; ip++)
2651 if (newpos[ip] > oldpos[ip] &&
2652 (newpos[ip] - oldpos[ip]) > added_chars)
2653 added_chars = newpos[ip] - oldpos[ip];
2654 if (line->size + added_chars > MAX_LINE - 2)
2655 return NULL;
2656 if (line == text->last_line) {
2657 if (line == TEMP_LINE(text, 0))
2658 mod_line = TEMP_LINE(text, 1);
2659 else
2660 mod_line = TEMP_LINE(text, 0);
2661 } else {
2662 allocHTLine(mod_line, (unsigned) (line->size + added_chars));
2663 }
2664 if (!mod_line)
2665 return NULL;
2666 if (!*prev_anchor)
2667 *prev_anchor = text->first_anchor;
2668 head_processed = (*prev_anchor && (*prev_anchor)->line_num < line_number);
2669 memcpy(mod_line, line, LINE_SIZE(0));
2670 t = newdata = mod_line->data;
2671 ip = 0;
2672 while (ip <= ninserts) {
2673 /* line->size is in bytes, so it may be larger than needed... */
2674 int curlim = (ip < ninserts
2675 ? oldpos[ip]
2676 : ((int) line->size <= MAX_LINE
2677 ? MAX_LINE + 1
2678 : (int) line->size + 1));
2679
2680 pre = s;
2681
2682 /* Fast forward to char==curlim or EOL. Stop *before* the
2683 style-change chars. */
2684 while (*s) {
2685 if (text && text->T.output_utf8
2686 && UCH(*s) >= 0x80 && UCH(*s) < 0xC0) {
2687 pre = s + 1;
2688 } else if (!IsSpecialAttrChar(*s)) { /* At a "displayed" char */
2689 if (ioldc >= curlim)
2690 break;
2691 ioldc++;
2692 pre = s + 1;
2693 }
2694 s++;
2695 }
2696
2697 /* Now s is at the "displayed" char, pre is before the style change */
2698 if (ip) /* Fix anchor positions */
2699 move_anchors_in_region(line, line_number, prev_anchor /*updates++ */ ,
2700 &head_processed,
2701 (int) (copied - line->data), (int) (pre - line->data),
2702 shift);
2703 #if defined(USE_COLOR_STYLE) /* Move styles too */
2704 #define NStyle mod_line->styles[istyle]
2705 for (;
2706 istyle < line->numstyles && (int) NStyle.sc_horizpos < curlim;
2707 istyle++)
2708 /* Should not we include OFF-styles at curlim? */
2709 NStyle.sc_horizpos = CAST_POS(NStyle.sc_horizpos + shift);
2710 #endif
2711 while (copied < pre) /* Copy verbatim to byte == pre */
2712 *t++ = *copied++;
2713 if (ip < ninserts) { /* Insert spaces */
2714 int delta = newpos[ip] - oldpos[ip] - shift;
2715
2716 if (delta < 0) { /* Not used yet? */
2717 while (delta++ < 0 && t > newdata && t[-1] == ' ')
2718 t--, shift--;
2719 } else
2720 shift = newpos[ip] - oldpos[ip];
2721 while (delta-- > 0)
2722 *t++ = ' ';
2723 }
2724 ip++;
2725 }
2726 while (pre < s) /* Copy remaining style-codes */
2727 *t++ = *pre++;
2728 /* Check whether the last anchor continues on the next line */
2729 if (head_processed
2730 && *prev_anchor
2731 && (*prev_anchor)->line_num == line_number) {
2732 (*prev_anchor)->extent = (short) ((*prev_anchor)->extent + shift);
2733 }
2734 *t = '\0';
2735 mod_line->size = (unsigned short) (t - newdata);
2736 return mod_line;
2737 }
2738
2739 #if defined(USE_COLOR_STYLE)
2740 #define direction2s(d) ((d) == STACK_OFF \
2741 ? "OFF" \
2742 : ((d) == STACK_ON \
2743 ? "ON" \
2744 : "*ON"))
2745
2746 /*
2747 * Found an OFF change not part of an adjacent matched pair.
2748 *
2749 * Walk backward looking for the corresponding ON change.
2750 * Move everything after split_pos to be at split_pos.
2751 *
2752 * This can only work correctly if all changes are correctly nested! If this
2753 * fails, assume it is safer to leave whatever comes before the OFF on the
2754 * previous line alone.
2755 */
skip_matched_and_correct_offsets(HTStyleChange * end,HTStyleChange * start,unsigned split_pos)2756 static HTStyleChange *skip_matched_and_correct_offsets(HTStyleChange *end,
2757 HTStyleChange *start,
2758 unsigned split_pos)
2759 {
2760 HTStyleChange *result = 0;
2761 int level = 0;
2762 HTStyleChange *tmp = end;
2763
2764 CTRACE_STYLE((tfp, "SKIP Style %d %d (%s), split %u\n",
2765 tmp->sc_horizpos,
2766 tmp->sc_style,
2767 direction2s(tmp->sc_direction),
2768 split_pos));
2769 for (; tmp >= start; tmp--) {
2770 CTRACE_STYLE((tfp, "... %d %d (%s)\n",
2771 tmp->sc_horizpos,
2772 tmp->sc_style,
2773 direction2s(tmp->sc_direction)));
2774 if (tmp->sc_style == end->sc_style) {
2775 if (tmp->sc_direction == STACK_OFF) {
2776 level--;
2777 } else if (tmp->sc_direction == STACK_ON) {
2778 if (++level == 0) {
2779 result = tmp;
2780 break;
2781 }
2782 } else {
2783 break;
2784 }
2785 }
2786 if (tmp->sc_horizpos > split_pos) {
2787 tmp->sc_horizpos = CAST_POS(split_pos);
2788 }
2789 }
2790 return result;
2791 }
2792 #endif /* USE_COLOR_STYLE */
2793
2794 #define reset_horizpos(value) value = 0, value ^= MASK_POS
2795
split_line(HText * text,unsigned split)2796 static void split_line(HText *text, unsigned split)
2797 {
2798 HTStyle *style = text->style;
2799 int spare;
2800 int indent = (text->in_line_1
2801 ? text->style->indent1st
2802 : text->style->leftIndent);
2803 int new_offset;
2804 short alignment;
2805 TextAnchor *a;
2806 int CurLine = text->Lines;
2807 int HeadTrim = 0;
2808 int SpecialAttrChars = 0;
2809 int TailTrim = 0;
2810 int s, s_post, s_pre, t_underline = underline_on, t_bold = bold_on;
2811 char *p;
2812 char *cp;
2813 int ctrl_chars_on_previous_line = 0;
2814
2815 #ifndef WIDEC_CURSES
2816 int utfxtra_on_previous_line = UTFXTRA_ON_THIS_LINE;
2817 #endif
2818
2819 HTLine *previous = text->last_line;
2820 HTLine *line;
2821
2822 /*
2823 * Set new line.
2824 */
2825 if (previous == TEMP_LINE(text, 0))
2826 line = TEMP_LINE(text, 1);
2827 else
2828 line = TEMP_LINE(text, 0);
2829 if (line == NULL)
2830 return;
2831 memset(line, 0, (size_t) LINE_SIZE(0));
2832
2833 ctrl_chars_on_this_line = 0; /*reset since we are going to a new line */
2834 utfxtra_on_this_line = 0; /*reset too, we'll count them */
2835 text->LastChar = ' ';
2836
2837 #ifdef DEBUG_APPCH
2838 CTRACE((tfp, "GridText: split_line(%p,%d) called\n", text, split));
2839 CTRACE((tfp, " previous=%s\n", previous->data));
2840 CTRACE((tfp, " bold_on=%d, underline_on=%d\n", bold_on, underline_on));
2841 #endif
2842
2843 cp = previous->data;
2844
2845 /* Float LY_SOFT_NEWLINE to the start */
2846 if (cp[0] == LY_BOLD_START_CHAR
2847 || cp[0] == LY_UNDERLINE_START_CHAR) {
2848 switch (cp[1]) {
2849 case LY_SOFT_NEWLINE:
2850 cp[1] = cp[0];
2851 cp[0] = LY_SOFT_NEWLINE;
2852 break;
2853 case LY_BOLD_START_CHAR:
2854 case LY_UNDERLINE_START_CHAR:
2855 if (cp[2] == LY_SOFT_NEWLINE) {
2856 cp[2] = cp[1];
2857 cp[1] = cp[0];
2858 cp[0] = LY_SOFT_NEWLINE;
2859 }
2860 break;
2861 }
2862 }
2863 if (split > previous->size) {
2864 CTRACE((tfp,
2865 "*** split_line: split==%u greater than last_line->size==%d !\n",
2866 split, previous->size));
2867 if (split > MAX_LINE) {
2868 split = previous->size;
2869 if ((cp = strrchr(previous->data, ' ')) &&
2870 cp - previous->data > 1)
2871 split = (unsigned) (cp - previous->data);
2872 CTRACE((tfp, " split adjusted to %u.\n", split));
2873 }
2874 }
2875
2876 text->Lines++;
2877
2878 previous->next->prev = line;
2879 line->prev = previous;
2880 line->next = previous->next;
2881 previous->next = line;
2882 text->last_line = line;
2883 line->size = 0;
2884 line->offset = 0;
2885 text->permissible_split = 0; /* 12/13/93 */
2886 line->data[0] = '\0';
2887
2888 alignment = style->alignment;
2889
2890 if (split > 0) { /* Restore flags to the value at the splitting point */
2891 if (!(dump_output_immediately && use_underscore))
2892 t_underline = set_style_by_embedded_chars(previous->data,
2893 previous->data + split,
2894 LY_UNDERLINE_START_CHAR, LY_UNDERLINE_END_CHAR);
2895
2896 t_bold = set_style_by_embedded_chars(previous->data,
2897 previous->data + split,
2898 LY_BOLD_START_CHAR, LY_BOLD_END_CHAR);
2899
2900 }
2901
2902 if (!(dump_output_immediately && use_underscore) && t_underline) {
2903 line->data[line->size++] = LY_UNDERLINE_START_CHAR;
2904 line->data[line->size] = '\0';
2905 ctrl_chars_on_this_line++;
2906 SpecialAttrChars++;
2907 }
2908 if (t_bold) {
2909 line->data[line->size++] = LY_BOLD_START_CHAR;
2910 line->data[line->size] = '\0';
2911 ctrl_chars_on_this_line++;
2912 SpecialAttrChars++;
2913 }
2914
2915 /*
2916 * Split at required point
2917 */
2918 if (split > 0) { /* Delete space at "split" splitting line */
2919 char *prevdata = previous->data, *linedata = line->data;
2920 unsigned plen;
2921 int i;
2922
2923 /* Split the line. -FM */
2924 prevdata[previous->size] = '\0';
2925 previous->size = (unsigned short) split;
2926
2927 /*
2928 * Trim any spaces or soft hyphens from the beginning
2929 * of our new line. -FM
2930 */
2931 p = prevdata + split;
2932 while (((*p == ' '
2933 #ifdef USE_JUSTIFY_ELTS
2934 /* if justification is allowed for prev line, then raw
2935 * HT_NON_BREAK_SPACE are still present in data[] (they'll be
2936 * substituted at the end of this function with ' ') - VH
2937 */
2938 || *p == HT_NON_BREAK_SPACE
2939 #endif
2940 )
2941 && (HeadTrim || text->first_anchor ||
2942 underline_on || bold_on ||
2943 alignment != HT_LEFT ||
2944 style->wordWrap || style->freeFormat ||
2945 style->spaceBefore || style->spaceAfter)) ||
2946 *p == LY_SOFT_HYPHEN) {
2947 p++;
2948 HeadTrim++;
2949 }
2950
2951 plen = (unsigned) strlen(p);
2952 if (plen) { /* Count funny characters */
2953 for (i = (int) (plen - 1); i >= 0; i--) {
2954 if (p[i] == LY_UNDERLINE_START_CHAR ||
2955 p[i] == LY_UNDERLINE_END_CHAR ||
2956 p[i] == LY_BOLD_START_CHAR ||
2957 p[i] == LY_BOLD_END_CHAR ||
2958 p[i] == LY_SOFT_HYPHEN) {
2959 ctrl_chars_on_this_line++;
2960 } else if (IS_UTF_EXTRA(p[i])) {
2961 utfxtra_on_this_line++;
2962 }
2963 if (p[i] == LY_SOFT_HYPHEN &&
2964 (int) text->permissible_split < i)
2965 text->permissible_split = (unsigned) (i + 1);
2966 }
2967 ctrl_chars_on_this_line += utfxtra_on_this_line;
2968
2969 /* Add the data to the new line. -FM */
2970 strcat(linedata, p);
2971 line->size = (unsigned short) (line->size + plen);
2972 }
2973 }
2974
2975 /*
2976 * Economize on space.
2977 */
2978 p = previous->data + previous->size - 1;
2979 while (p >= previous->data
2980 && (*p == ' '
2981 #ifdef USE_JUSTIFY_ELTS
2982 /* if justification is allowed for prev line, then raw
2983 * HT_NON_BREAK_SPACE are still present in data[] (they'll be
2984 * substituted at the end of this function with ' ') - VH
2985 */
2986 || *p == HT_NON_BREAK_SPACE
2987 #endif
2988 )
2989 #ifdef USE_PRETTYSRC
2990 && !psrc_view /*don't strip trailing whites - since next line can
2991 start with LY_SOFT_NEWLINE - so we don't lose spaces when
2992 'p'rinting this text to file -VH */
2993 #endif
2994 && (ctrl_chars_on_this_line || HeadTrim || text->first_anchor ||
2995 underline_on || bold_on ||
2996 alignment != HT_LEFT ||
2997 style->wordWrap || style->freeFormat ||
2998 style->spaceBefore || style->spaceAfter)) {
2999 p--; /* Strip trailers. */
3000 }
3001 /* Strip trailers. */
3002 TailTrim = (int) (previous->data + previous->size - 1 - p);
3003 previous->size = (unsigned short) (previous->size - TailTrim);
3004 p[1] = '\0';
3005
3006 /*
3007 * s is the effective split position, given by either a non-zero
3008 * value of split or by the size of the previous line before
3009 * trimming. - kw
3010 */
3011 if (split == 0) {
3012 s = previous->size + TailTrim; /* the original size */
3013 } else {
3014 s = (int) split;
3015 }
3016 s_post = s + HeadTrim;
3017 s_pre = s - TailTrim;
3018
3019 #ifdef DEBUG_SPLITLINE
3020 #ifdef DEBUG_APPCH
3021 if (s != (int) split)
3022 #endif
3023 CTRACE((tfp, "GridText: split_line(%u [now:%d]) called\n", split, s));
3024 #endif
3025
3026 #if defined(USE_COLOR_STYLE)
3027 if (previous->styles == stylechanges_buffers[0])
3028 line->styles = stylechanges_buffers[1];
3029 else
3030 line->styles = stylechanges_buffers[0];
3031 line->numstyles = 0;
3032 {
3033 HTStyleChange *from = previous->styles + previous->numstyles - 1;
3034 HTStyleChange *to = line->styles + MAX_STYLES_ON_LINE - 1;
3035 HTStyleChange *scan, *at_end;
3036
3037 /* Color style changes after the split position
3038 * are transferred to the new line. Ditto for changes
3039 * in the trimming region, but we stop when we reach an OFF change.
3040 * The second loop below may then handle remaining changes. - kw */
3041 while (from >= previous->styles && to >= line->styles) {
3042 *to = *from;
3043 if ((int) to->sc_horizpos > s_post) {
3044 to->sc_horizpos = CAST_POS(to->sc_horizpos
3045 + SpecialAttrChars
3046 - s_post);
3047 } else if ((int) to->sc_horizpos > s_pre &&
3048 (to->sc_direction == STACK_ON ||
3049 to->sc_direction == ABS_ON)) {
3050 if ((int) to->sc_horizpos < s)
3051 to->sc_horizpos = 0;
3052 else
3053 to->sc_horizpos = CAST_POS(SpecialAttrChars);
3054 } else {
3055 break;
3056 }
3057 to--;
3058 from--;
3059 }
3060 /* FROM may be invalid, otherwise it is either an ON change at or
3061 before s_pre, or is an OFF change at or before s_post. */
3062
3063 scan = from;
3064 at_end = from;
3065 /* Now on the previous line we have a correctly nested but
3066 possibly non-terminated sequence of style changes.
3067 Terminate it, and duplicate unterminated changes at the
3068 beginning of the new line. */
3069 while (scan >= previous->styles && at_end >= previous->styles) {
3070 /* The algorithm: scan back though the styles on the previous line.
3071 a) If OFF, skip the matched group.
3072 Report a bug on failure.
3073 b) If ON, (try to) cancel the corresponding ON at at_end,
3074 and the corresponding OFF at to;
3075 If not, put the corresponding OFF at at_end, and copy to to;
3076 */
3077 if (scan->sc_direction == STACK_OFF) {
3078 scan = skip_matched_and_correct_offsets(scan, previous->styles,
3079 (unsigned) s_pre);
3080 if (!scan) {
3081 CTRACE((tfp, "BUG: styles improperly nested.\n"));
3082 break;
3083 }
3084 } else if (scan->sc_direction == STACK_ON) {
3085 if (at_end->sc_direction == STACK_ON
3086 && at_end->sc_style == scan->sc_style
3087 && (int) at_end->sc_horizpos >= s_pre)
3088 at_end--;
3089 else if (at_end >= previous->styles + MAX_STYLES_ON_LINE - 1) {
3090 CTRACE((tfp, "BUG: style overflow before split_line.\n"));
3091 break;
3092 } else {
3093 at_end++;
3094 at_end->sc_direction = STACK_OFF;
3095 at_end->sc_style = scan->sc_style;
3096 at_end->sc_horizpos = CAST_POS(s_pre);
3097 CTRACE_STYLE((tfp,
3098 "split_line, %d:style[%d] %d (dir=%d)\n",
3099 s_pre,
3100 (int) (at_end - from),
3101 scan->sc_style,
3102 at_end->sc_direction));
3103 }
3104 if (to < line->styles + MAX_STYLES_ON_LINE - 1
3105 && to[1].sc_direction == STACK_OFF
3106 && to[1].sc_horizpos <= (unsigned) SpecialAttrChars
3107 && to[1].sc_style == scan->sc_style)
3108 to++;
3109 else if (to >= line->styles) {
3110 *to = *scan;
3111 to->sc_horizpos = CAST_POS(SpecialAttrChars);
3112 to--;
3113 } else {
3114 CTRACE((tfp, "BUG: style overflow after split_line.\n"));
3115 break;
3116 }
3117 }
3118 if ((int) scan->sc_horizpos > s_pre) {
3119 scan->sc_horizpos = CAST_POS(s_pre);
3120 }
3121 scan--;
3122 }
3123 line->numstyles = (unsigned short) (line->styles
3124 + MAX_STYLES_ON_LINE
3125 - 1 - to);
3126 if (line->numstyles > 0 && line->numstyles < MAX_STYLES_ON_LINE) {
3127 int n;
3128
3129 for (n = 0; n < line->numstyles; n++)
3130 line->styles[n] = to[n + 1];
3131 } else if (line->numstyles == 0) {
3132 reset_horizpos(line->styles[0].sc_horizpos);
3133 }
3134 previous->numstyles = (unsigned short) (at_end - previous->styles + 1);
3135 if (previous->numstyles == 0) {
3136 reset_horizpos(previous->styles[0].sc_horizpos);
3137 }
3138 }
3139 #endif /*USE_COLOR_STYLE */
3140
3141 {
3142 HTLine *temp;
3143
3144 allocHTLine(temp, previous->size);
3145 if (!temp)
3146 outofmem(__FILE__, "split_line_2");
3147
3148 memcpy(temp, previous, LINE_SIZE(previous->size));
3149 #if defined(USE_COLOR_STYLE)
3150 POOLallocstyles(temp->styles, previous->numstyles);
3151 if (!temp->styles)
3152 outofmem(__FILE__, "split_line_2");
3153 memcpy(temp->styles, previous->styles, sizeof(HTStyleChange) * previous->numstyles);
3154 #endif
3155 previous = temp;
3156 }
3157
3158 previous->prev->next = previous; /* Link in new line */
3159 previous->next->prev = previous; /* Could be same node of course */
3160
3161 /*
3162 * Terminate finished line for printing.
3163 */
3164 previous->data[previous->size] = '\0';
3165
3166 /*
3167 * Align left, right or center.
3168 */
3169 spare = 0;
3170 if (
3171 #ifdef USE_JUSTIFY_ELTS
3172 this_line_was_split ||
3173 #endif
3174 (alignment == HT_CENTER ||
3175 alignment == HT_RIGHT) || text->stbl) {
3176 /* Calculate spare character positions if needed */
3177 for (cp = previous->data; *cp; cp++) {
3178 if (*cp == LY_UNDERLINE_START_CHAR ||
3179 *cp == LY_UNDERLINE_END_CHAR ||
3180 *cp == LY_BOLD_START_CHAR ||
3181 *cp == LY_BOLD_END_CHAR ||
3182 #ifndef WIDEC_CURSES
3183 IS_UTF_EXTRA(*cp) ||
3184 #endif
3185 *cp == LY_SOFT_HYPHEN) {
3186 ctrl_chars_on_previous_line++;
3187 }
3188 }
3189 if ((previous->size > 0) &&
3190 (int) (previous->data[previous->size - 1] == LY_SOFT_HYPHEN))
3191 ctrl_chars_on_previous_line--;
3192
3193 /* @@ first line indent */
3194 #ifdef WIDEC_CURSES
3195 spare = WRAP_COLS(text)
3196 - (int) style->rightIndent
3197 - indent
3198 + ctrl_chars_on_previous_line
3199 - LYstrExtent2(previous->data, previous->size);
3200 if (spare < 0 && LYwideLines) /* Can be wider than screen */
3201 spare = 0;
3202 #else
3203 spare = WRAP_COLS(text)
3204 - (int) style->rightIndent
3205 - indent
3206 + ctrl_chars_on_previous_line
3207 - previous->size;
3208 if (spare < 0 && LYwideLines) /* Can be wider than screen */
3209 spare = 0;
3210
3211 if (spare > 0 && !dump_output_immediately &&
3212 text->T.output_utf8 && ctrl_chars_on_previous_line) {
3213 utfxtra_on_previous_line -= UTFXTRA_ON_THIS_LINE;
3214 if (utfxtra_on_previous_line) {
3215 int spare_cu = (LYcols_cu(text) -
3216 utfxtra_on_previous_line - indent +
3217 ctrl_chars_on_previous_line - previous->size);
3218
3219 /*
3220 * Shift non-leftaligned UTF-8 lines that would be
3221 * mishandled by the display library towards the left
3222 * if this would make them fit. The resulting display
3223 * will not be as intended, but this is better than
3224 * having them split by curses. (Curses cursor movement
3225 * optimization may still cause wrong positioning within
3226 * the line, in particular after a sequence of spaces).
3227 * - kw
3228 */
3229 if (spare_cu < spare) {
3230 if (spare_cu >= 0) {
3231 if (alignment == HT_CENTER &&
3232 (int) (previous->offset + indent + spare / 2 +
3233 previous->size)
3234 - ctrl_chars_on_previous_line
3235 + utfxtra_on_previous_line <= LYcols_cu(text))
3236 /* do nothing - it still fits - kw */ ;
3237 else {
3238 spare = spare_cu;
3239 }
3240 } else if (indent + (int) previous->offset + spare_cu >= 0) { /* subtract overdraft from effective indentation */
3241 indent += (int) previous->offset + spare_cu;
3242 previous->offset = 0;
3243 spare = 0;
3244 }
3245 }
3246 }
3247 }
3248 #endif
3249 }
3250
3251 new_offset = previous->offset;
3252 switch (style->alignment) {
3253 case HT_CENTER:
3254 new_offset += indent + spare / 2;
3255 break;
3256 case HT_RIGHT:
3257 new_offset += indent + spare;
3258 break;
3259 case HT_LEFT:
3260 case HT_JUSTIFY: /* Not implemented */
3261 default:
3262 new_offset += indent;
3263 break;
3264 } /* switch */
3265 previous->offset = (unsigned short) ((new_offset < 0) ? 0 : new_offset);
3266
3267 if (text->stbl) {
3268 /*
3269 * Notify simple table stuff of line split, so that it can
3270 * set the last cell's length. The last cell should and
3271 * its row should really end here, or on one of the following
3272 * lines with no more characters added after the break.
3273 * We don't know whether a cell has been started, so ignore
3274 * errors here.
3275 * This call is down here because we need the
3276 * ctrl_chars_on_previous_line, which have just been re-
3277 * counted above. - kw
3278 */
3279 Stbl_lineBreak(text->stbl,
3280 text->Lines - 1,
3281 previous->offset,
3282 previous->size - ctrl_chars_on_previous_line);
3283 }
3284
3285 text->in_line_1 = NO; /* unless caller sets it otherwise */
3286
3287 /*
3288 * If we split the line, adjust the anchor
3289 * structure values for the new line. -FM
3290 */
3291
3292 if (s > 0) { /* if not completely empty */
3293 int moved = 0;
3294
3295 /* In the algorithm below we move or not move anchors between
3296 lines using some heuristic criteria. However, it is
3297 desirable not to have two consequent anchors on different
3298 lines *in a wrong order*! (How can this happen?)
3299 So when the "reasonable choice" is not unique, we use the
3300 MOVED flag to choose one.
3301 */
3302 /* Our operations can make a non-empty all-whitespace link
3303 empty. So what? */
3304 if ((a = text->last_anchor_before_split) == 0)
3305 a = text->first_anchor;
3306
3307 for (; a; a = a->next) {
3308 if (a->line_num == CurLine) {
3309 int len = a->extent, n = a->number, start = a->line_pos;
3310 int end = start + len;
3311
3312 text->last_anchor_before_split = a;
3313
3314 /* Which anchors do we leave on the previous line?
3315 a) empty finished (We need a cut-off value.
3316 "Just because": those before s;
3317 this is the only case when we use s, not s_pre/s_post);
3318 b) Those which start before s_pre;
3319 */
3320 if (start < s_pre) {
3321 if (end <= s_pre)
3322 continue; /* No problem */
3323
3324 CTRACE_SPLITLINE((tfp, "anchor %d: no relocation", n));
3325 if (end > s_post) {
3326 CTRACE_SPLITLINE((tfp, " of the start.\n"));
3327 a->extent = (short) (a->extent
3328 - (TailTrim + HeadTrim)
3329 + SpecialAttrChars);
3330 } else {
3331 CTRACE_SPLITLINE((tfp, ", cut the end.\n"));
3332 a->extent = (short) (s_pre - start);
3333 }
3334 continue;
3335 } else if (start < s && !len
3336 && (!n || (a->show_anchor && !moved))) {
3337 CTRACE_SPLITLINE((tfp,
3338 "anchor %d: no relocation, empty-finished",
3339 n));
3340 a->line_pos = (short) s_pre; /* Leave at the end of line */
3341 continue;
3342 }
3343
3344 /* The rest we relocate */
3345 moved = 1;
3346 a->line_num++;
3347 CTRACE_SPLITLINE((tfp,
3348 "anchor %d: (T,H,S)=(%d,%d,%d); (line,pos,ext):(%d,%d,%d), ",
3349 n, TailTrim, HeadTrim, SpecialAttrChars,
3350 a->line_num, a->line_pos, a->extent));
3351 if (end < s_post) { /* Move the end to s_post */
3352 CTRACE_SPLITLINE((tfp, "Move end +%d, ", s_post - end));
3353 len += s_post - end;
3354 }
3355 if (start < s_post) { /* Move the start to s_post */
3356 CTRACE_SPLITLINE((tfp, "Move start +%d, ", s_post - start));
3357 len -= s_post - start;
3358 start = s_post;
3359 }
3360 a->line_pos = (short) (start - s_post + SpecialAttrChars);
3361 a->extent = (short) len;
3362
3363 CTRACE_SPLITLINE((tfp, "->(%d,%d,%d)\n",
3364 a->line_num, a->line_pos, a->extent));
3365 } else if (a->line_num > CurLine)
3366 break;
3367 }
3368 }
3369 #ifdef USE_JUSTIFY_ELTS
3370 /* now perform justification - by VH */
3371
3372 if (this_line_was_split
3373 && spare > 0
3374 && !text->stbl /* We don't inform TRST on the cell width change yet */
3375 && justify_max_void_percent > 0
3376 && justify_max_void_percent <= 100
3377 && justify_max_void_percent >= ((100 * spare)
3378 / (WRAP_COLS(text)
3379 - (int) style->rightIndent
3380 - indent
3381 + ctrl_chars_on_previous_line))) {
3382 /* this is the only case when we need justification */
3383 char *jp = previous->data + justify_start_position;
3384 ht_run_info *r = ht_runs;
3385 char c;
3386 int d_, r_;
3387 HTLine *jline;
3388
3389 ht_num_runs = 0;
3390 r->byte_len = r->cell_len = 0;
3391
3392 for (; (c = *jp) != 0; ++jp) {
3393 if (c == ' ') {
3394 ++r;
3395 ++ht_num_runs;
3396 r->byte_len = r->cell_len = 0;
3397 continue;
3398 }
3399 ++r->byte_len;
3400 if (IsSpecialAttrChar(c))
3401 continue;
3402
3403 ++r->cell_len;
3404 if (c == HT_NON_BREAK_SPACE) {
3405 *jp = ' '; /* substitute it */
3406 continue;
3407 }
3408 if (text->T.output_utf8 && is8bits(c)) {
3409 int utf_extra = (int) utf8_length(text->T.output_utf8, jp);
3410
3411 r->byte_len += utf_extra;
3412 jp += utf_extra;
3413 }
3414 }
3415 ++ht_num_runs;
3416
3417 if (ht_num_runs != 1) {
3418 int *oldpos = (int *) malloc(sizeof(int)
3419 * 2 * (size_t) (ht_num_runs - 1));
3420 int *newpos = oldpos + ht_num_runs - 1;
3421 int i = 1;
3422
3423 if (oldpos == NULL)
3424 outofmem(__FILE__, "split_line_3");
3425
3426 d_ = spare / (ht_num_runs - 1);
3427 r_ = spare % (ht_num_runs - 1);
3428
3429 /* The first run is not moved, proceed to the second one */
3430 oldpos[0] = justify_start_position + ht_runs[0].cell_len + 1;
3431 newpos[0] = oldpos[0] + (d_ + (r_-- > 0));
3432 while (i < ht_num_runs - 1) {
3433 int delta = ht_runs[i].cell_len + 1;
3434
3435 oldpos[i] = oldpos[i - 1] + delta;
3436 newpos[i] = newpos[i - 1] + delta + (d_ + (r_-- > 0));
3437 i++;
3438 }
3439 jline = insert_blanks_in_line(previous, CurLine, text,
3440 &last_anchor_of_previous_line /*updates++ */ ,
3441 ht_num_runs - 1, oldpos, newpos);
3442 free(oldpos);
3443 if (jline == NULL)
3444 outofmem(__FILE__, "split_line_4");
3445 previous->next->prev = jline;
3446 previous->prev->next = jline;
3447
3448 freeHTLine(text, previous);
3449
3450 previous = jline;
3451 }
3452 if (justify_start_position) {
3453 char *p2 = previous->data;
3454
3455 for (; p2 < previous->data + justify_start_position; ++p2)
3456 *p2 = (char) (*p2 == HT_NON_BREAK_SPACE ? ' ' : *p2);
3457 }
3458 } else {
3459 if (REALLY_CAN_JUSTIFY(text)) {
3460 char *p2;
3461
3462 /* it was permitted to justify line, but this function was called
3463 * to end paragraph - we must substitute HT_NON_BREAK_SPACEs with
3464 * spaces in previous line
3465 */
3466 if (line->size && !text->stbl) {
3467 CTRACE((tfp,
3468 "BUG: justification: shouldn't happen - new line is not empty!\n\t'%.*s'\n",
3469 line->size, line->data));
3470 }
3471
3472 for (p2 = previous->data; *p2; ++p2)
3473 if (*p2 == HT_NON_BREAK_SPACE)
3474 *p2 = ' ';
3475 } else if (have_raw_nbsps) {
3476 /* this is very rare case, that can happen in forms placed in
3477 table cells */
3478 unsigned i;
3479
3480 for (i = 0; i < previous->size; ++i)
3481 if (previous->data[i] == HT_NON_BREAK_SPACE)
3482 previous->data[i] = ' ';
3483
3484 /*next line won't be justified, so substitute nbsps in it too */
3485 for (i = 0; i < line->size; ++i)
3486 if (line->data[i] == HT_NON_BREAK_SPACE)
3487 line->data[i] = ' ';
3488 }
3489
3490 /* else HT_NON_BREAK_SPACEs were substituted with spaces in
3491 HText_appendCharacter */
3492 }
3493 /* cleanup */
3494 can_justify_this_line = TRUE;
3495 justify_start_position = 0;
3496 this_line_was_split = FALSE;
3497 have_raw_nbsps = FALSE;
3498 #endif /* USE_JUSTIFY_ELTS */
3499 return;
3500 } /* split_line */
3501
3502 #ifdef DEBUG_SPLITLINE
do_new_line(HText * text,const char * fn,int ln)3503 static void do_new_line(HText *text, const char *fn, int ln)
3504 {
3505 CTRACE_SPLITLINE((tfp, "new_line %s@%d\n", fn, ln));
3506 split_line(text, 0);
3507 }
3508
3509 #define new_line(text) do_new_line(text, __FILE__, __LINE__)
3510 #else
3511 #define new_line(text) split_line(text, 0)
3512 #endif
3513
3514 /* Allow vertical blank space
3515 * --------------------------
3516 */
blank_lines(HText * text,int newlines)3517 static void blank_lines(HText *text, int newlines)
3518 {
3519 if (HText_TrueEmptyLine(text->last_line, text, FALSE)) { /* No text on current line */
3520 HTLine *line = text->last_line->prev;
3521 BOOL first = (BOOL) (line == text->last_line);
3522
3523 if (no_title && first)
3524 return;
3525
3526 #ifdef USE_COLOR_STYLE
3527 /* Style-change petty requests at the start of the document: */
3528 if (first && newlines == 1)
3529 return; /* Do not add a blank line at start */
3530 #endif
3531
3532 while (line != NULL &&
3533 line != text->last_line &&
3534 HText_TrueEmptyLine(line, text, FALSE)) {
3535 if (newlines == 0)
3536 break;
3537 newlines--; /* Don't bother: already blank */
3538 line = line->prev;
3539 }
3540 } else {
3541 newlines++; /* Need also to finish this line */
3542 }
3543
3544 for (; newlines; newlines--) {
3545 new_line(text);
3546 }
3547 text->in_line_1 = YES;
3548 }
3549
3550 /* New paragraph in current style
3551 * ------------------------------
3552 * See also: setStyle.
3553 */
HText_appendParagraph(HText * text)3554 void HText_appendParagraph(HText *text)
3555 {
3556 int after = text->style->spaceAfter;
3557 int before = text->style->spaceBefore;
3558
3559 blank_lines(text, ((after > before) ? after : before));
3560 }
3561
3562 /* Set Style
3563 * ---------
3564 *
3565 * Does not filter unnecessary style changes.
3566 */
HText_setStyle(HText * text,HTStyle * style)3567 void HText_setStyle(HText *text, HTStyle *style)
3568 {
3569 int after, before;
3570
3571 if (!style)
3572 return; /* Safety */
3573 after = text->style->spaceAfter;
3574 before = style->spaceBefore;
3575
3576 CTRACE((tfp, "GridText: Change to style %s\n", GetHTStyleName(style)));
3577
3578 blank_lines(text, ((after > before) ? after : before));
3579
3580 text->style = style;
3581 }
3582
3583 /* Append a character to the text object
3584 * -------------------------------------
3585 */
HText_appendCharacter(HText * text,int ch)3586 void HText_appendCharacter(HText *text, int ch)
3587 {
3588 HTLine *line;
3589 HTStyle *style;
3590 int indent;
3591 int actual;
3592
3593 #ifdef DEBUG_APPCH
3594 #ifdef CJK_EX
3595 static unsigned char save_ch = 0;
3596 #endif
3597
3598 if (TRACE) {
3599 char *special = NULL; /* make trace a little more readable */
3600
3601 switch (ch) {
3602 case HT_NON_BREAK_SPACE:
3603 special = "HT_NON_BREAK_SPACE";
3604 break;
3605 case HT_EN_SPACE:
3606 special = "HT_EN_SPACE";
3607 break;
3608 case LY_UNDERLINE_START_CHAR:
3609 special = "LY_UNDERLINE_START_CHAR";
3610 break;
3611 case LY_UNDERLINE_END_CHAR:
3612 special = "LY_UNDERLINE_END_CHAR";
3613 break;
3614 case LY_BOLD_START_CHAR:
3615 special = "LY_BOLD_START_CHAR";
3616 break;
3617 case LY_BOLD_END_CHAR:
3618 special = "LY_BOLD_END_CHAR";
3619 break;
3620 case LY_SOFT_HYPHEN:
3621 special = "LY_SOFT_HYPHEN";
3622 break;
3623 case LY_SOFT_NEWLINE:
3624 special = "LY_SOFT_NEWLINE";
3625 break;
3626 default:
3627 special = NULL;
3628 break;
3629 }
3630
3631 if (special != NULL) {
3632 CTRACE((tfp, "add(%s %d special char) %d/%d\n", special, ch,
3633 HTisDocumentSource(), HTOutputFormat != WWW_SOURCE));
3634 } else {
3635 #ifdef CJK_EX /* 1998/08/30 (Sun) 13:26:23 */
3636 if (save_ch == 0) {
3637 if (IS_SJIS_HI1(ch) || IS_SJIS_HI2(ch)) {
3638 save_ch = ch;
3639 } else {
3640 CTRACE((tfp, "add(%c) %d/%d\n", ch,
3641 HTisDocumentSource(), HTOutputFormat != WWW_SOURCE));
3642 }
3643 } else {
3644 CTRACE((tfp, "add(%c%c) %d/%d\n", save_ch, ch,
3645 HTisDocumentSource(), HTOutputFormat != WWW_SOURCE));
3646 save_ch = 0;
3647 }
3648 #else
3649 if (UCH(ch) < 0x80) {
3650 CTRACE((tfp, "add(%c) %d/%d\n", UCH(ch),
3651 HTisDocumentSource(), HTOutputFormat != WWW_SOURCE));
3652 } else {
3653 CTRACE((tfp, "add(%02x) %d/%d\n", UCH(ch),
3654 HTisDocumentSource(), HTOutputFormat != WWW_SOURCE));
3655 }
3656 #endif /* CJK_EX */
3657 }
3658 } /* trace only */
3659 #endif /* DEBUG_APPCH */
3660
3661 /*
3662 * Make sure we don't crash on NULLs.
3663 */
3664 if (!text)
3665 return;
3666
3667 if (text->halted > 1) {
3668 /*
3669 * We should stop outputting more text, because low memory was
3670 * detected. - kw
3671 */
3672 if (text->halted == 2) {
3673 /*
3674 * But if we haven't done so yet, first append a warning.
3675 * We should still have a few bytes left for that :).
3676 * We temporarily reset test->halted to 0 for this, since
3677 * this function will get called recursively. - kw
3678 */
3679 text->halted = 0;
3680 text->kanji_buf = '\0';
3681 HText_appendText(text, gettext(" *** MEMORY EXHAUSTED ***"));
3682 }
3683 text->halted = 3;
3684 return;
3685 }
3686 #ifdef USE_TH_JP_AUTO_DETECT
3687 if ((HTCJK == JAPANESE) && (text->detected_kcode != DET_MIXED) &&
3688 (text->specified_kcode != SJIS) && (text->specified_kcode != EUC)) {
3689 unsigned char c;
3690 eDetectedKCode save_d_kcode;
3691
3692 c = UCH(ch);
3693 save_d_kcode = text->detected_kcode;
3694 switch (text->SJIS_status) {
3695 case SJIS_state_has_bad_code:
3696 break;
3697 case SJIS_state_neutral:
3698 if (IS_SJIS_HI1(c) || IS_SJIS_HI2(c)) {
3699 text->SJIS_status = SJIS_state_in_kanji;
3700 } else if ((c & 0x80) && !IS_SJIS_X0201KANA(c)) {
3701 text->SJIS_status = SJIS_state_has_bad_code;
3702 if (text->EUC_status == EUC_state_has_bad_code)
3703 text->detected_kcode = DET_MIXED;
3704 else
3705 text->detected_kcode = DET_EUC;
3706 }
3707 break;
3708 case SJIS_state_in_kanji:
3709 if (IS_SJIS_LO(c)) {
3710 text->SJIS_status = SJIS_state_neutral;
3711 } else {
3712 text->SJIS_status = SJIS_state_has_bad_code;
3713 if (text->EUC_status == EUC_state_has_bad_code)
3714 text->detected_kcode = DET_MIXED;
3715 else
3716 text->detected_kcode = DET_EUC;
3717 }
3718 break;
3719 }
3720 switch (text->EUC_status) {
3721 case EUC_state_has_bad_code:
3722 break;
3723 case EUC_state_neutral:
3724 if (IS_EUC_HI(c)) {
3725 text->EUC_status = EUC_state_in_kanji;
3726 } else if (c == 0x8e) {
3727 text->EUC_status = EUC_state_in_kana;
3728 } else if (c & 0x80) {
3729 text->EUC_status = EUC_state_has_bad_code;
3730 if (text->SJIS_status == SJIS_state_has_bad_code)
3731 text->detected_kcode = DET_MIXED;
3732 else
3733 text->detected_kcode = DET_SJIS;
3734 }
3735 break;
3736 case EUC_state_in_kanji:
3737 if (IS_EUC_LOX(c)) {
3738 text->EUC_status = EUC_state_neutral;
3739 } else {
3740 text->EUC_status = EUC_state_has_bad_code;
3741 if (text->SJIS_status == SJIS_state_has_bad_code)
3742 text->detected_kcode = DET_MIXED;
3743 else
3744 text->detected_kcode = DET_SJIS;
3745 }
3746 break;
3747 case EUC_state_in_kana:
3748 if ((0xA1 <= c) && (c <= 0xDF)) {
3749 text->EUC_status = EUC_state_neutral;
3750 } else {
3751 text->EUC_status = EUC_state_has_bad_code;
3752 if (text->SJIS_status == SJIS_state_has_bad_code)
3753 text->detected_kcode = DET_MIXED;
3754 else
3755 text->detected_kcode = DET_SJIS;
3756 }
3757 break;
3758 }
3759 if (save_d_kcode != text->detected_kcode) {
3760 switch (text->detected_kcode) {
3761 case DET_SJIS:
3762 CTRACE((tfp,
3763 "TH_JP_AUTO_DETECT: This document's kcode seems SJIS.\n"));
3764 break;
3765 case DET_EUC:
3766 CTRACE((tfp,
3767 "TH_JP_AUTO_DETECT: This document's kcode seems EUC.\n"));
3768 break;
3769 case DET_MIXED:
3770 CTRACE((tfp,
3771 "TH_JP_AUTO_DETECT: This document's kcode seems mixed!\n"));
3772 break;
3773 default:
3774 CTRACE((tfp,
3775 "TH_JP_AUTO_DETECT: This document's kcode is unexpected!\n"));
3776 break;
3777 }
3778 }
3779 }
3780 #endif /* USE_TH_JP_AUTO_DETECT */
3781 /*
3782 * Make sure we don't hang on escape sequences.
3783 */
3784 if (ch == CH_ESC && !IS_CJK_TTY) { /* decimal 27 S/390 -- gil -- 1504 */
3785 return;
3786 }
3787 #ifndef USE_SLANG
3788 /*
3789 * Block 8-bit chars not allowed by the current display character
3790 * set if they are below what LYlowest_eightbit indicates.
3791 * Slang used its own replacements, so for USE_SLANG blocking here
3792 * is not necessary to protect terminals from those characters.
3793 * They should have been filtered out or translated by an earlier
3794 * processing stage anyway. - kw
3795 */
3796 #ifndef EBCDIC /* S/390 -- gil -- 1514 */
3797 if (is8bits(ch) && !IS_CJK_TTY &&
3798 !text->T.transp && !text->T.output_utf8 &&
3799 UCH(ch) < LYlowest_eightbit[current_char_set]) {
3800 return;
3801 }
3802 #endif /* EBCDIC */
3803 #endif /* !USE_SLANG */
3804 if (UCH(ch) == 155 && !IS_CJK_TTY) { /* octal 233 */
3805 if (!HTPassHighCtrlRaw &&
3806 !text->T.transp && !text->T.output_utf8 &&
3807 (155 < LYlowest_eightbit[current_char_set])) {
3808 return;
3809 }
3810 }
3811
3812 line = text->last_line;
3813 style = text->style;
3814
3815 indent = text->in_line_1 ? (int) style->indent1st : (int) style->leftIndent;
3816
3817 if (IS_CJK_TTY) {
3818 switch (text->state) {
3819 case S_text:
3820 if (ch == CH_ESC) { /* S/390 -- gil -- 1536 */
3821 /*
3822 * Setting up for CJK escape sequence handling (based on
3823 * Takuya ASADA's (asada@three-a.co.jp) CJK Lynx). -FM
3824 */
3825 text->state = S_esc;
3826 text->kanji_buf = '\0';
3827 return;
3828 }
3829 break;
3830
3831 case S_esc:
3832 /*
3833 * Expecting '$'or '(' following CJK ESC.
3834 */
3835 if (ch == '$') {
3836 text->state = S_dollar;
3837 return;
3838 } else if (ch == '(') {
3839 text->state = S_paren;
3840 return;
3841 } else {
3842 text->state = S_text;
3843 }
3844 /* FALLTHRU */
3845
3846 case S_dollar:
3847 /*
3848 * Expecting '@', 'B', 'A' or '(' after CJK "ESC$".
3849 */
3850 if (ch == '@' || ch == 'B' || ch == 'A') {
3851 text->state = S_nonascii_text;
3852 if (ch == '@' || ch == 'B')
3853 text->kcode = JIS;
3854 return;
3855 } else if (ch == '(') {
3856 text->state = S_dollar_paren;
3857 return;
3858 } else {
3859 text->state = S_text;
3860 }
3861 break;
3862
3863 case S_dollar_paren:
3864 /*
3865 * Expecting 'C' after CJK "ESC$(".
3866 */
3867 if (ch == 'C') {
3868 text->state = S_nonascii_text;
3869 return;
3870 } else {
3871 text->state = S_text;
3872 }
3873 break;
3874
3875 case S_paren:
3876 /*
3877 * Expecting 'B', 'J', 'T' or 'I' after CJK "ESC(".
3878 */
3879 if (ch == 'B' || ch == 'J' || ch == 'T') {
3880 /*
3881 * Can split here. -FM
3882 */
3883 text->permissible_split = text->last_line->size;
3884 text->state = S_text;
3885 return;
3886 } else if (ch == 'I') {
3887 text->state = S_jisx0201_text;
3888 /*
3889 * Can split here. -FM
3890 */
3891 text->permissible_split = text->last_line->size;
3892 text->kcode = JIS;
3893 return;
3894 } else {
3895 text->state = S_text;
3896 }
3897 break;
3898
3899 case S_nonascii_text:
3900 /*
3901 * Expecting CJK ESC after non-ASCII text.
3902 */
3903 if (ch == CH_ESC) { /* S/390 -- gil -- 1553 */
3904 text->state = S_esc;
3905 text->kanji_buf = '\0';
3906 if (HTCJK == JAPANESE) {
3907 text->kcode = NOKANJI;
3908 }
3909 return;
3910 } else if (UCH(ch) < 32) {
3911 text->state = S_text;
3912 text->kanji_buf = '\0';
3913 if (HTCJK == JAPANESE) {
3914 text->kcode = NOKANJI;
3915 }
3916 } else {
3917 ch |= 0200;
3918 }
3919 break;
3920
3921 /*
3922 * JIS X0201 Kana in JIS support. - by ASATAKU
3923 */
3924 case S_jisx0201_text:
3925 if (ch == CH_ESC) { /* S/390 -- gil -- 1570 */
3926 text->state = S_esc;
3927 text->kanji_buf = '\0';
3928 text->kcode = NOKANJI;
3929 return;
3930 } else {
3931 text->kanji_buf = '\216';
3932 ch |= 0200;
3933 }
3934 break;
3935 } /* end switch */
3936
3937 if (!text->kanji_buf) {
3938 if ((ch & 0200) != 0) {
3939 /*
3940 * JIS X0201 Kana in SJIS support. - by ASATAKU
3941 */
3942 if ((text->kcode != JIS)
3943 #ifdef USE_TH_JP_AUTO_DETECT
3944 && (text->specified_kcode != EUC)
3945 && (text->detected_kcode != DET_EUC)
3946 #endif
3947 && (
3948 #ifdef KANJI_CODE_OVERRIDE
3949 (last_kcode == SJIS) ||
3950 ((last_kcode == NOKANJI) &&
3951 #endif
3952 ((text->kcode == SJIS) ||
3953 #ifdef USE_TH_JP_AUTO_DETECT
3954 ((text->detected_kcode == DET_SJIS) &&
3955 (text->specified_kcode == NOKANJI)) ||
3956 #endif
3957 ((text->kcode == NOKANJI) &&
3958 (text->specified_kcode == SJIS)))
3959 #ifdef KANJI_CODE_OVERRIDE
3960 )
3961 #endif
3962 ) &&
3963 (UCH(ch) >= 0xA1) &&
3964 (UCH(ch) <= 0xDF)) {
3965 if (conv_jisx0201kana) {
3966 unsigned char c = UCH(ch);
3967 unsigned char kb = UCH(text->kanji_buf);
3968
3969 JISx0201TO0208_SJIS(c,
3970 (unsigned char *) &kb,
3971 (unsigned char *) &c);
3972 ch = (char) c;
3973 text->kanji_buf = kb;
3974 }
3975 /* 1998/01/19 (Mon) 09:06:15 */
3976 text->permissible_split = (int) text->last_line->size;
3977 } else {
3978 text->kanji_buf = ch;
3979 /*
3980 * Can split here. -FM
3981 */
3982 text->permissible_split = text->last_line->size;
3983 return;
3984 }
3985 }
3986 } else {
3987 goto check_WrapSource;
3988 }
3989 } else if (ch == CH_ESC) { /* S/390 -- gil -- 1587 */
3990 return;
3991 }
3992 #ifdef CJK_EX /* MOJI-BAKE Fix! 1997/10/12 -- 10/31 (Fri) 00:22:57 - JH7AYN */
3993 if (IS_CJK_TTY && /* added condition - kw */
3994 (ch == LY_BOLD_START_CHAR || ch == LY_BOLD_END_CHAR)) {
3995 text->permissible_split = (int) line->size; /* Can split here */
3996 if (HTCJK == JAPANESE)
3997 text->kcode = NOKANJI;
3998 }
3999 #endif
4000
4001 if (IsSpecialAttrChar(ch) && ch != LY_SOFT_NEWLINE) {
4002 #if !defined(USE_COLOR_STYLE) || !defined(NO_DUMP_WITH_BACKSPACES)
4003 if (line->size >= (MAX_LINE - 1)) {
4004 return;
4005 }
4006 #if defined(USE_COLOR_STYLE) && !defined(NO_DUMP_WITH_BACKSPACES)
4007 if (with_backspaces && !IS_CJK_TTY && !text->T.output_utf8) {
4008 #endif
4009 if (ch == LY_UNDERLINE_START_CHAR) {
4010 line->data[line->size++] = LY_UNDERLINE_START_CHAR;
4011 line->data[line->size] = '\0';
4012 underline_on = TRUE;
4013 if (!(dump_output_immediately && use_underscore))
4014 ctrl_chars_on_this_line++;
4015 return;
4016 } else if (ch == LY_UNDERLINE_END_CHAR) {
4017 line->data[line->size++] = LY_UNDERLINE_END_CHAR;
4018 line->data[line->size] = '\0';
4019 underline_on = FALSE;
4020 if (!(dump_output_immediately && use_underscore))
4021 ctrl_chars_on_this_line++;
4022 return;
4023 } else if (ch == LY_BOLD_START_CHAR) {
4024 line->data[line->size++] = LY_BOLD_START_CHAR;
4025 line->data[line->size] = '\0';
4026 bold_on = TRUE;
4027 ctrl_chars_on_this_line++;
4028 return;
4029 } else if (ch == LY_BOLD_END_CHAR) {
4030 line->data[line->size++] = LY_BOLD_END_CHAR;
4031 line->data[line->size] = '\0';
4032 bold_on = FALSE;
4033 ctrl_chars_on_this_line++;
4034 return;
4035 } else if (ch == LY_SOFT_HYPHEN) {
4036 int i;
4037
4038 /*
4039 * Ignore the soft hyphen if it is the first character
4040 * on the line, or if it is preceded by a space or
4041 * hyphen. -FM
4042 */
4043 if (line->size < 1 || text->permissible_split >= line->size) {
4044 return;
4045 }
4046
4047 for (i = (int) (text->permissible_split + 1);
4048 line->data[i];
4049 i++) {
4050 if (!IsSpecialAttrChar(UCH(line->data[i])) &&
4051 !isspace(UCH(line->data[i])) &&
4052 UCH(line->data[i]) != '-' &&
4053 UCH(line->data[i]) != HT_NON_BREAK_SPACE &&
4054 UCH(line->data[i]) != HT_EN_SPACE) {
4055 break;
4056 }
4057 }
4058 if (line->data[i] == '\0') {
4059 return;
4060 }
4061 }
4062 #if defined(USE_COLOR_STYLE) && !defined(NO_DUMP_WITH_BACKSPACES)
4063 } else {
4064 /* if (with_backspaces && HTCJK==HTNOCJK && !text->T.output_utf8) */
4065 return;
4066 }
4067 #endif
4068
4069 #else
4070 return;
4071 #endif
4072 } else if (ch == LY_SOFT_NEWLINE) {
4073 line->data[line->size++] = LY_SOFT_NEWLINE;
4074 line->data[line->size] = '\0';
4075 return;
4076 }
4077
4078 if (text->T.output_utf8) {
4079 /*
4080 * Some extra checks for UTF-8 output here to make sure
4081 * memory is not overrun. For a non-first char, append
4082 * to the line here and return. - kw
4083 */
4084 if (IS_UTF_EXTRA(ch)) {
4085 if ((line->size > (MAX_LINE - 1))
4086 || (indent + (int) (line->offset + line->size)
4087 + UTFXTRA_ON_THIS_LINE
4088 - ctrl_chars_on_this_line
4089 + ((line->size > 0) &&
4090 (int) (line->data[line->size - 1] ==
4091 LY_SOFT_HYPHEN ?
4092 1 : 0)) >= LYcols_cu(text))
4093 ) {
4094 if (!text->permissible_split || text->source) {
4095 text->permissible_split = line->size;
4096 while (text->permissible_split > 0 &&
4097 IS_UTF_EXTRA(line->data[text->permissible_split - 1]))
4098 text->permissible_split--;
4099 if (text->permissible_split &&
4100 (line->data[text->permissible_split - 1] & 0x80))
4101 text->permissible_split--;
4102 if (text->permissible_split == line->size)
4103 text->permissible_split = 0;
4104 }
4105 split_line(text, text->permissible_split);
4106 line = text->last_line;
4107 if (text->source && line->size - ctrl_chars_on_this_line
4108 + UTFXTRA_ON_THIS_LINE == 0)
4109 HText_appendCharacter(text, LY_SOFT_NEWLINE);
4110 }
4111 line->data[line->size++] = (char) ch;
4112 line->data[line->size] = '\0';
4113 utfxtra_on_this_line++;
4114 ctrl_chars_on_this_line++;
4115 return;
4116 } else if (ch & 0x80) { /* a first char of UTF-8 sequence - kw */
4117 if ((line->size > (MAX_LINE - 7))) {
4118 if (!text->permissible_split || text->source) {
4119 text->permissible_split = line->size;
4120 while (text->permissible_split > 0 &&
4121 (line->data[text->permissible_split - 1] & 0xc0)
4122 == 0x80) {
4123 text->permissible_split--;
4124 }
4125 if (text->permissible_split == line->size)
4126 text->permissible_split = 0;
4127 }
4128 split_line(text, text->permissible_split);
4129 line = text->last_line;
4130 if (text->source && line->size - ctrl_chars_on_this_line
4131 + UTFXTRA_ON_THIS_LINE == 0)
4132 HText_appendCharacter(text, LY_SOFT_NEWLINE);
4133 }
4134 }
4135 }
4136
4137 /*
4138 * New Line.
4139 */
4140 if (ch == '\n') {
4141 new_line(text);
4142 text->in_line_1 = YES; /* First line of new paragraph */
4143 /*
4144 * There are some pages written in
4145 * different kanji codes. - TA & kw
4146 */
4147 if (HTCJK == JAPANESE)
4148 text->kcode = NOKANJI;
4149 return;
4150 }
4151
4152 /*
4153 * Convert EN_SPACE to a space here so that it doesn't get collapsed.
4154 */
4155 if (ch == HT_EN_SPACE)
4156 ch = ' ';
4157
4158 #ifdef SH_EX /* 1997/11/01 (Sat) 12:08:54 */
4159 if (ch == 0x0b) { /* ^K ??? */
4160 ch = '\r';
4161 }
4162 if (ch == 0x1a) { /* ^Z ??? */
4163 ch = '\r';
4164 }
4165 #endif
4166
4167 /*
4168 * I'm going to cheat here in a BIG way. Since I know that all
4169 * \r's will be trapped by HTML_put_character I'm going to use
4170 * \r to mean go down a line but don't start a new paragraph.
4171 * i.e., use the second line indenting.
4172 */
4173 if (ch == '\r') {
4174 new_line(text);
4175 text->in_line_1 = NO;
4176 /*
4177 * There are some pages written in
4178 * different kanji codes. - TA & kw
4179 */
4180 if (HTCJK == JAPANESE)
4181 text->kcode = NOKANJI;
4182 return;
4183 }
4184
4185 /*
4186 * Tabs.
4187 */
4188 if (ch == '\t') {
4189 const HTTabStop *Tab;
4190 int target, target_cu; /* Where to tab to */
4191 int here, here_cu; /* in _cu we try to guess what curses thinks */
4192
4193 if (line->size > 0 && line->data[line->size - 1] == LY_SOFT_HYPHEN) {
4194 /*
4195 * A tab shouldn't follow a soft hyphen, so
4196 * if one does, we'll dump the soft hyphen. -FM
4197 */
4198 line->data[--line->size] = '\0';
4199 ctrl_chars_on_this_line--;
4200 }
4201 here = ((int) (line->size + line->offset) + indent)
4202 - ctrl_chars_on_this_line; /* Consider special chars GAB */
4203 here_cu = here + UTFXTRA_ON_THIS_LINE;
4204 if (style->tabs) { /* Use tab table */
4205 for (Tab = style->tabs;
4206 Tab->position <= here;
4207 Tab++) {
4208 if (!Tab->position) {
4209 new_line(text);
4210 return;
4211 }
4212 }
4213 target = Tab->position;
4214 } else if (text->in_line_1) { /* Use 2nd indent */
4215 if (here >= (int) style->leftIndent) {
4216 new_line(text); /* wrap */
4217 return;
4218 } else {
4219 target = (int) style->leftIndent;
4220 }
4221 } else { /* Default tabs align with left indent mod 8 */
4222 #ifdef DEFAULT_TABS_8
4223 target = (((int) line->offset + (int) line->size + 8) & (-8))
4224 + (int) style->leftIndent;
4225 #else
4226 new_line(text);
4227 return;
4228 #endif
4229 }
4230
4231 if (target >= here)
4232 target_cu = target;
4233 else
4234 target_cu = target + (here_cu - here);
4235
4236 if (target > WRAP_COLS(text) - (int) style->rightIndent &&
4237 HTOutputFormat != WWW_SOURCE) {
4238 new_line(text);
4239 } else {
4240 /*
4241 * Can split here. -FM
4242 */
4243 text->permissible_split = line->size;
4244 if (target_cu > WRAP_COLS(text))
4245 target -= target_cu - WRAP_COLS(text);
4246 if (line->size == 0) {
4247 line->offset = (unsigned short) (line->offset + (target - here));
4248 } else {
4249 for (; here < target; here++) {
4250 /* Put character into line */
4251 line->data[line->size++] = ' ';
4252 line->data[line->size] = '\0';
4253 }
4254 }
4255 }
4256 return;
4257 }
4258 /* if tab */
4259 check_WrapSource:
4260 if ((text->source || dont_wrap_pre) && text == HTMainText) {
4261 /*
4262 * If we're displaying document source, wrap long lines to keep all of
4263 * the source visible.
4264 */
4265 int target = (int) (line->offset + line->size) - ctrl_chars_on_this_line;
4266 int target_cu = target + UTFXTRA_ON_THIS_LINE;
4267
4268 if (target >= WRAP_COLS(text) - style->rightIndent -
4269 ((IS_CJK_TTY && text->kanji_buf) ? 1 : 0) ||
4270 (text->T.output_utf8 &&
4271 target_cu + UTF_XLEN(ch) >= LYcols_cu(text))) {
4272 int saved_kanji_buf;
4273 eGridState saved_state;
4274 BOOL add_blank = (dont_wrap_pre
4275 && line->size
4276 && (line->data[line->size - 1] == ' '));
4277
4278 new_line(text);
4279 line = text->last_line;
4280
4281 saved_kanji_buf = text->kanji_buf;
4282 saved_state = text->state;
4283 text->kanji_buf = '\0';
4284 text->state = S_text;
4285 HText_appendCharacter(text, LY_SOFT_NEWLINE);
4286 if (add_blank)
4287 HText_appendCharacter(text, ' ');
4288 text->kanji_buf = saved_kanji_buf;
4289 text->state = saved_state;
4290 }
4291 }
4292
4293 if (ch == ' ') {
4294 /*
4295 * Can split here. -FM
4296 */
4297 text->permissible_split = text->last_line->size;
4298 /*
4299 * There are some pages written in
4300 * different kanji codes. - TA
4301 */
4302 if (HTCJK == JAPANESE)
4303 text->kcode = NOKANJI;
4304 }
4305
4306 /*
4307 * Check for end of line.
4308 *
4309 * Notes:
4310 * 1) text->permissible_split is nonzero if we found a place to split the
4311 * line. If there is no such place, we still will wrap at the display
4312 * limits (the comparison against LYcols_cu). Furthermore, if the
4313 * curses-pads feature is active, we will ignore the first comparison
4314 * (against WRAP_COLS) to allow wide preformatted text to be displayed
4315 * without wrapping.
4316 * 2) ctrl_chars_on_this_line are nonprintable bytes used for formatting.
4317 */
4318 actual = ((indent + (int) line->offset + (int) line->size) +
4319 ((line->size > 0) &&
4320 (int) (line->data[line->size - 1] == LY_SOFT_HYPHEN ? 1 : 0))
4321 - ctrl_chars_on_this_line);
4322
4323 if ((
4324 #if !defined(USE_SLANG) && !defined(PDCURSES)
4325 (text->permissible_split
4326 #ifdef USE_CURSES_PADS
4327 || !LYwideLines
4328 #endif
4329 ) &&
4330 #endif
4331 (actual
4332 + (int) style->rightIndent
4333 + ((IS_CJK_TTY && text->kanji_buf) ? 1 : 0)
4334 ) >= WRAP_COLS(text))
4335 || (text->T.output_utf8
4336 && ((actual
4337 + UTFXTRA_ON_THIS_LINE
4338 + UTF_XLEN(ch)
4339 ) > (LYcols_cu(text) - 1)))) {
4340
4341 if (style->wordWrap && HTOutputFormat != WWW_SOURCE) {
4342 #ifdef USE_JUSTIFY_ELTS
4343 if (REALLY_CAN_JUSTIFY(text))
4344 this_line_was_split = TRUE;
4345 #endif
4346 split_line(text, text->permissible_split);
4347 if (ch == ' ') {
4348 return; /* Ignore space causing split */
4349 }
4350
4351 } else if (HTOutputFormat == WWW_SOURCE) {
4352 /*
4353 * For source output we don't want to wrap this stuff
4354 * unless absolutely necessary. - LJM
4355 * !
4356 * If we don't wrap here we might get a segmentation fault.
4357 * but let's see what happens
4358 */
4359 if ((int) line->size >= (int) (MAX_LINE - 1)) {
4360 new_line(text); /* try not to linewrap */
4361 }
4362 } else {
4363 /*
4364 * For normal stuff like pre let's go ahead and
4365 * wrap so the user can see all of the text.
4366 */
4367 if ((dump_output_immediately || (crawl && traversal))
4368 && dont_wrap_pre) {
4369 if ((int) line->size >= (int) (MAX_LINE - 1)) {
4370 new_line(text);
4371 }
4372 } else {
4373 new_line(text);
4374 }
4375 }
4376 } else if ((int) line->size >= (int) (MAX_LINE - 1)) {
4377 /*
4378 * Never overrun memory if DISPLAY_COLS is set to a large value - KW
4379 */
4380 new_line(text);
4381 }
4382
4383 /*
4384 * Insert normal characters.
4385 */
4386 if (ch == HT_NON_BREAK_SPACE
4387 #ifdef USE_JUSTIFY_ELTS
4388 && !REALLY_CAN_JUSTIFY(text)
4389 #endif
4390 )
4391 ch = ' ';
4392 #ifdef USE_JUSTIFY_ELTS
4393 else
4394 have_raw_nbsps = TRUE;
4395 #endif
4396
4397 /* we leave raw HT_NON_BREAK_SPACE otherwise (we'll substitute it later) */
4398
4399 if (ch & 0x80)
4400 text->have_8bit_chars = YES;
4401
4402 /*
4403 * Kanji charactor handling.
4404 */
4405 {
4406 HTFont font = style->font;
4407 unsigned char hi, lo, tmp[2];
4408
4409 line = text->last_line; /* May have changed */
4410
4411 if (IS_CJK_TTY && text->kanji_buf) {
4412 hi = UCH(text->kanji_buf);
4413 lo = UCH(ch);
4414
4415 if (HTCJK == JAPANESE) {
4416 if (text->kcode != JIS) {
4417 if (IS_SJIS_2BYTE(hi, lo)) {
4418 if (IS_EUC(hi, lo)) {
4419 #ifdef KANJI_CODE_OVERRIDE
4420 if (last_kcode != NOKANJI)
4421 text->kcode = last_kcode;
4422 else
4423 #endif
4424 if (text->specified_kcode != NOKANJI)
4425 text->kcode = text->specified_kcode;
4426 #ifdef USE_TH_JP_AUTO_DETECT
4427 else if (text->detected_kcode == DET_EUC)
4428 text->kcode = EUC;
4429 else if (text->detected_kcode == DET_SJIS)
4430 text->kcode = SJIS;
4431 #endif
4432 else if (IS_EUC_X0201KANA(hi, lo) &&
4433 (text->kcode != EUC))
4434 text->kcode = SJIS;
4435 } else
4436 text->kcode = SJIS;
4437 } else if (IS_EUC(hi, lo))
4438 text->kcode = EUC;
4439 else
4440 text->kcode = NOKANJI;
4441 }
4442
4443 switch (kanji_code) {
4444 case EUC:
4445 if (text->kcode == SJIS) {
4446 SJIS_TO_EUC1(hi, lo, tmp);
4447 line->data[line->size++] = (char) tmp[0];
4448 line->data[line->size++] = (char) tmp[1];
4449 } else if (IS_EUC(hi, lo)) {
4450 if (conv_jisx0201kana) {
4451 JISx0201TO0208_EUC(hi, lo, &hi, &lo);
4452 }
4453 line->data[line->size++] = (char) hi;
4454 line->data[line->size++] = (char) lo;
4455 } else {
4456 CTRACE((tfp,
4457 "This character (%X:%X) doesn't seem Japanese\n",
4458 hi, lo));
4459 line->data[line->size++] = '=';
4460 line->data[line->size++] = '=';
4461 }
4462 break;
4463
4464 case SJIS:
4465 if ((text->kcode == EUC) || (text->kcode == JIS)) {
4466 if (!conv_jisx0201kana && IS_EUC_X0201KANA(hi, lo))
4467 line->data[line->size++] = (char) lo;
4468 else {
4469 EUC_TO_SJIS1(hi, lo, tmp);
4470 line->data[line->size++] = (char) tmp[0];
4471 line->data[line->size++] = (char) tmp[1];
4472 }
4473 } else if (IS_SJIS_2BYTE(hi, lo)) {
4474 line->data[line->size++] = (char) hi;
4475 line->data[line->size++] = (char) lo;
4476 } else {
4477 line->data[line->size++] = '=';
4478 line->data[line->size++] = '=';
4479 CTRACE((tfp,
4480 "This character (%X:%X) doesn't seem Japanese\n",
4481 hi, lo));
4482 }
4483 break;
4484
4485 default:
4486 break;
4487 }
4488 } else {
4489 line->data[line->size++] = (char) hi;
4490 line->data[line->size++] = (char) lo;
4491 }
4492 text->kanji_buf = 0;
4493 } else if (!conv_jisx0201kana
4494 && (HTCJK == JAPANESE)
4495 && IS_SJIS_X0201KANA(UCH((ch))) &&
4496 (kanji_code == EUC)) {
4497 line->data[line->size++] = (char) UCH(0x8e);
4498 line->data[line->size++] = (char) ch;
4499 } else if (IS_CJK_TTY) {
4500 line->data[line->size++] = (char) ((kanji_code != NOKANJI) ?
4501 ch :
4502 (font & HT_CAPITALS) ?
4503 TOUPPER(ch) : ch);
4504 } else {
4505 line->data[line->size++] = /* Put character into line */
4506 (char) (font & HT_CAPITALS ? TOUPPER(ch) : ch);
4507 }
4508 line->data[line->size] = '\0';
4509 if (font & HT_DOUBLE) /* Do again if doubled */
4510 HText_appendCharacter(text, HT_NON_BREAK_SPACE);
4511 /* NOT a permissible split */
4512
4513 if (ch == LY_SOFT_HYPHEN) {
4514 ctrl_chars_on_this_line++;
4515 /*
4516 * Can split here. -FM
4517 */
4518 text->permissible_split = text->last_line->size;
4519 }
4520 if (ch == LY_SOFT_NEWLINE) {
4521 ctrl_chars_on_this_line++;
4522 }
4523 }
4524 return;
4525 }
4526
4527 #ifdef USE_COLOR_STYLE
4528 /* Insert a style change into the current line
4529 * -------------------------------------------
4530 */
_internal_HTC(HText * text,int style,int dir)4531 void _internal_HTC(HText *text, int style, int dir)
4532 {
4533 HTLine *line;
4534
4535 /* can't change style if we have no text to change style with */
4536 if (text != 0) {
4537
4538 line = text->last_line;
4539
4540 if (line->numstyles > 0 && dir == 0 &&
4541 line->styles[line->numstyles - 1].sc_direction &&
4542 line->styles[line->numstyles - 1].sc_style == (unsigned) style &&
4543 (int) line->styles[line->numstyles - 1].sc_horizpos
4544 == (int) line->size - ctrl_chars_on_this_line) {
4545 /*
4546 * If this is an OFF change directly preceded by an
4547 * ON for the same style, just remove the previous one. - kw
4548 */
4549 line->numstyles--;
4550 } else if (line->numstyles < MAX_STYLES_ON_LINE) {
4551 line->styles[line->numstyles].sc_horizpos = CAST_POS(line->size);
4552 /*
4553 * Special chars for bold and underlining usually don't
4554 * occur with color style, but soft hyphen can.
4555 * And in UTF-8 display mode all non-initial bytes are
4556 * counted as ctrl_chars. - kw
4557 */
4558 if ((int) line->styles[line->numstyles].sc_horizpos >= ctrl_chars_on_this_line) {
4559 line->styles[line->numstyles].sc_horizpos =
4560 CAST_POS(line->styles[line->numstyles].sc_horizpos
4561 - ctrl_chars_on_this_line);
4562 }
4563 line->styles[line->numstyles].sc_style = (unsigned short) style;
4564 line->styles[line->numstyles].sc_direction = CAST_DIR(dir);
4565 CTRACE_STYLE((tfp, "internal_HTC %d:style[%d] %d (dir=%d)\n",
4566 line->size,
4567 line->numstyles,
4568 style,
4569 dir));
4570 line->numstyles++;
4571 }
4572 }
4573 }
4574 #endif
4575
4576 /* Set LastChar element in the text object.
4577 * ----------------------------------------
4578 */
HText_setLastChar(HText * text,int ch)4579 void HText_setLastChar(HText *text, int ch)
4580 {
4581 if (!text)
4582 return;
4583
4584 text->LastChar = (char) ch;
4585 }
4586
4587 /* Get LastChar element in the text object.
4588 * ----------------------------------------
4589 */
HText_getLastChar(HText * text)4590 char HText_getLastChar(HText *text)
4591 {
4592 if (!text)
4593 return ('\0');
4594
4595 return ((char) text->LastChar);
4596 }
4597
4598 /* Simple table handling - private
4599 * -------------------------------
4600 */
4601
4602 /*
4603 * HText_insertBlanksInStblLines fixes up table lines when simple table
4604 * processing is closed, by calling insert_blanks_in_line for lines
4605 * that need fixup. Also recalculates alignment for those lines,
4606 * does additional updating of anchor positions, and makes sure the
4607 * display of the lines on screen will be updated after partial display
4608 * upon return to mainloop. - kw
4609 */
HText_insertBlanksInStblLines(HText * me,int ncols)4610 static int HText_insertBlanksInStblLines(HText *me, int ncols)
4611 {
4612 HTLine *line;
4613 HTLine *mod_line, *first_line = NULL;
4614 int *oldpos;
4615 int *newpos;
4616 int ninserts, lineno;
4617 int last_lineno, first_lineno_pass2;
4618
4619 #ifdef EXP_NESTED_TABLES
4620 int last_nonempty = -1;
4621 #endif
4622 int added_chars_before = 0;
4623 int lines_changed = 0;
4624 int max_width = 0, indent, spare, table_offset;
4625 HTStyle *style;
4626 short alignment;
4627 int i = 0;
4628
4629 lineno = Stbl_getStartLine(me->stbl);
4630 if (lineno < 0 || lineno > me->Lines)
4631 return -1;
4632 /*
4633 * oldpos, newpos: allocate space for two int arrays.
4634 */
4635 oldpos = typecallocn(int, 2 * (size_t)ncols);
4636 if (!oldpos)
4637 return -1;
4638 else
4639 newpos = oldpos + ncols;
4640 for (line = FirstHTLine(me); i < lineno; line = line->next, i++) {
4641 if (!line) {
4642 free(oldpos);
4643 return -1;
4644 }
4645 }
4646 first_lineno_pass2 = last_lineno = me->Lines;
4647 for (; line && lineno <= last_lineno; line = line->next, lineno++) {
4648 ninserts = Stbl_getFixupPositions(me->stbl, lineno, oldpos, newpos);
4649 if (ninserts < 0)
4650 continue;
4651 if (!first_line) {
4652 first_line = line;
4653 first_lineno_pass2 = lineno;
4654 if (TRACE) {
4655 int ip;
4656
4657 CTRACE((tfp, "line %d first to adjust -- newpos:", lineno));
4658 for (ip = 0; ip < ncols; ip++)
4659 CTRACE((tfp, " %d", newpos[ip]));
4660 CTRACE((tfp, "\n"));
4661 }
4662 }
4663 if (line == me->last_line) {
4664 if (line->size == 0 || HText_TrueEmptyLine(line, me, FALSE))
4665 continue;
4666 /*
4667 * Last ditch effort to end the table with a line break,
4668 * if HTML_end_element didn't do it. - kw
4669 */
4670 if (first_line == line) /* obscure: all table on last line... */
4671 first_line = NULL;
4672 new_line(me);
4673 line = me->last_line->prev;
4674 if (first_line == NULL)
4675 first_line = line;
4676 }
4677 if (ninserts == 0) {
4678 /* Do it also for no positions (but not error) */
4679 int width = HText_TrueLineSize(line, me, FALSE);
4680
4681 if (width > max_width)
4682 max_width = width;
4683 #ifdef EXP_NESTED_TABLES
4684 if (nested_tables) {
4685 if (width && last_nonempty < lineno)
4686 last_nonempty = lineno;
4687 }
4688 #endif
4689 CTRACE((tfp, "line %d true/max width:%d/%d oldpos: NONE\n",
4690 lineno, width, max_width));
4691 continue;
4692 }
4693 mod_line = insert_blanks_in_line(line, lineno, me,
4694 &me->last_anchor_before_stbl /*updates++ */ ,
4695 ninserts, oldpos, newpos);
4696 if (mod_line) {
4697 if (line == me->last_line) {
4698 me->last_line = mod_line;
4699 } else {
4700 added_chars_before += (mod_line->size - line->size);
4701 }
4702 line->prev->next = mod_line;
4703 line->next->prev = mod_line;
4704 lines_changed++;
4705 if (line == first_line)
4706 first_line = mod_line;
4707 freeHTLine(me, line);
4708 line = mod_line;
4709 #ifdef DISP_PARTIAL
4710 /*
4711 * Make sure modified lines get fully re-displayed after
4712 * loading with partial display is done.
4713 */
4714 if (me->first_lineno_last_disp_partial >= 0) {
4715 if (me->first_lineno_last_disp_partial >= lineno) {
4716 ResetPartialLinenos(me);
4717 } else if (me->last_lineno_last_disp_partial >= lineno) {
4718 me->last_lineno_last_disp_partial = lineno - 1;
4719 }
4720 }
4721 #endif
4722 } {
4723 int width = HText_TrueLineSize(line, me, FALSE);
4724
4725 if (width > max_width)
4726 max_width = width;
4727 #ifdef EXP_NESTED_TABLES
4728 if (nested_tables) {
4729 if (width && last_nonempty < lineno)
4730 last_nonempty = lineno;
4731 }
4732 #endif
4733 if (TRACE) {
4734 int ip;
4735
4736 CTRACE((tfp, "line %d true/max width:%d/%d oldpos:",
4737 lineno, width, max_width));
4738 for (ip = 0; ip < ninserts; ip++)
4739 CTRACE((tfp, " %d", oldpos[ip]));
4740 CTRACE((tfp, "\n"));
4741 }
4742 }
4743 }
4744 /*
4745 * Line offsets have been set based on the paragraph style, and
4746 * have already been updated for centering or right-alignment
4747 * for each line in split_line. Here we want to undo all that, and
4748 * align the table as a whole (i.e. all lines for which
4749 * Stbl_getFixupPositions returned >= 0). All those lines have to
4750 * get the same offset, for the simple table formatting mechanism
4751 * to make sense, and that may not actually be the case at this point.
4752 *
4753 * What indentation and alignment do we want for the table as
4754 * a whole? Let's take most style properties from me->style.
4755 * With some luck, it is the appropriate style for the element
4756 * enclosing the TABLE. But let's take alignment from the attribute
4757 * of the TABLE itself instead, if it was specified.
4758 *
4759 * Note that this logic assumes that all lines have been finished
4760 * by split_line. The order of calls made by HTML_end_element for
4761 * HTML_TABLE should take care of this.
4762 */
4763 style = me->style;
4764 alignment = Stbl_getAlignment(me->stbl);
4765 if (alignment == HT_ALIGN_NONE)
4766 alignment = style->alignment;
4767 indent = style->leftIndent;
4768 /* Calculate spare character positions */
4769 spare = WRAP_COLS(me) -
4770 (int) style->rightIndent - indent - max_width;
4771 if (spare < 0 && (int) style->rightIndent + spare >= 0) {
4772 /*
4773 * Not enough room! But we can fit if we ignore right indentation,
4774 * so let's do that.
4775 */
4776 spare = 0;
4777 } else if (spare < 0) {
4778 spare += style->rightIndent; /* ignore right indent, but need more */
4779 }
4780 if (spare < 0 && indent + spare >= 0) {
4781 /*
4782 * Still not enough room. But we can move to the left.
4783 */
4784 indent += spare;
4785 spare = 0;
4786 } else if (spare < 0) {
4787 /*
4788 * Still not enough. Something went wrong. Try the best we
4789 * can do.
4790 */
4791 CTRACE((tfp,
4792 "BUG: insertBlanks: resulting table too wide by %d positions!\n",
4793 -spare));
4794 indent = spare = 0;
4795 }
4796 /*
4797 * Align left, right or center.
4798 */
4799 switch (alignment) {
4800 case HT_CENTER:
4801 table_offset = indent + spare / 2;
4802 break;
4803 case HT_RIGHT:
4804 table_offset = indent + spare;
4805 break;
4806 case HT_LEFT:
4807 case HT_JUSTIFY:
4808 default:
4809 table_offset = indent;
4810 break;
4811 } /* switch */
4812
4813 CTRACE((tfp, "changing offsets"));
4814 for (line = first_line, lineno = first_lineno_pass2;
4815 line && lineno <= last_lineno && line != me->last_line;
4816 line = line->next, lineno++) {
4817 ninserts = Stbl_getFixupPositions(me->stbl, lineno, oldpos, newpos);
4818 if (ninserts >= 0 && (int) line->offset != table_offset) {
4819 #ifdef DISP_PARTIAL
4820 /* As above make sure modified lines get fully re-displayed */
4821 if (me->first_lineno_last_disp_partial >= 0) {
4822 if (me->first_lineno_last_disp_partial >= lineno) {
4823 ResetPartialLinenos(me);
4824 } else if (me->last_lineno_last_disp_partial >= lineno) {
4825 me->last_lineno_last_disp_partial = lineno - 1;
4826 }
4827 }
4828 #endif
4829 CTRACE((tfp, " %d:%d", lineno, table_offset - line->offset));
4830 line->offset = (unsigned short) (table_offset > 0
4831 ? table_offset
4832 : 0);
4833 }
4834 }
4835 #ifdef EXP_NESTED_TABLES
4836 if (nested_tables) {
4837 if (max_width)
4838 Stbl_update_enclosing(me->stbl, max_width, last_nonempty);
4839 }
4840 #endif
4841 CTRACE((tfp, " %d:done\n", lineno));
4842 free(oldpos);
4843 return lines_changed;
4844 }
4845
4846 /* Simple table handling - public functions
4847 * ----------------------------------------
4848 */
4849
4850 /* Cancel simple table handling
4851 */
HText_cancelStbl(HText * me)4852 void HText_cancelStbl(HText *me)
4853 {
4854 if (!me || !me->stbl) {
4855 CTRACE((tfp, "cancelStbl: ignored.\n"));
4856 return;
4857 }
4858 CTRACE((tfp, "cancelStbl: ok, will do.\n"));
4859 #ifdef EXP_NESTED_TABLES
4860 if (nested_tables) {
4861 STable_info *stbl = me->stbl;
4862
4863 while (stbl) {
4864 STable_info *enclosing = Stbl_get_enclosing(stbl);
4865
4866 Stbl_free(stbl);
4867 stbl = enclosing;
4868 }
4869 } else
4870 #endif
4871 Stbl_free(me->stbl);
4872 me->stbl = NULL;
4873 }
4874
4875 /* Start simple table handling
4876 */
HText_startStblTABLE(HText * me,int alignment)4877 void HText_startStblTABLE(HText *me, int alignment)
4878 {
4879 if (me) {
4880 #ifdef EXP_NESTED_TABLES
4881 STable_info *current = me->stbl;
4882 #endif
4883
4884 #ifdef EXP_NESTED_TABLES
4885 if (nested_tables) {
4886 if (current)
4887 new_line(me);
4888 } else
4889 #endif
4890 {
4891 if (me->stbl) {
4892 HText_cancelStbl(me); /* auto cancel previously open table */
4893 }
4894 }
4895
4896 me->stbl = Stbl_startTABLE(alignment);
4897 if (me->stbl) {
4898 CTRACE((tfp, "startStblTABLE: started.\n"));
4899 #ifdef EXP_NESTED_TABLES
4900 if (nested_tables) {
4901 Stbl_set_enclosing(me->stbl, current, me->last_anchor_before_stbl);
4902 }
4903 #endif
4904 me->last_anchor_before_stbl = me->last_anchor;
4905 } else {
4906 CTRACE((tfp, "startStblTABLE: failed.\n"));
4907 }
4908 }
4909 }
4910
4911 #ifdef EXP_NESTED_TABLES
free_enclosed_stbl(HText * me)4912 static void free_enclosed_stbl(HText *me)
4913 {
4914 if (me != NULL && me->enclosed_stbl != NULL) {
4915 HTList *list = me->enclosed_stbl;
4916 STable_info *stbl;
4917
4918 while (NULL != (stbl = (STable_info *) HTList_nextObject(list))) {
4919 CTRACE((tfp, "endStblTABLE: finally free %p\n", (void *) me->stbl));
4920 Stbl_free(stbl);
4921 }
4922 HTList_delete(me->enclosed_stbl);
4923 me->enclosed_stbl = NULL;
4924 }
4925 }
4926
4927 #else
4928 #define free_enclosed_stbl(me) /* nothing */
4929 #endif
4930
4931 /* Finish simple table handling
4932 * Return TRUE if the table is nested inside another table.
4933 */
HText_endStblTABLE(HText * me)4934 BOOLEAN HText_endStblTABLE(HText *me)
4935 {
4936 int ncols, lines_changed = 0;
4937 STable_info *enclosing = NULL;
4938
4939 if (!me || !me->stbl) {
4940 CTRACE((tfp, "endStblTABLE: ignored.\n"));
4941 free_enclosed_stbl(me);
4942 return FALSE;
4943 }
4944 CTRACE((tfp, "endStblTABLE: ok, will try.\n"));
4945
4946 ncols = Stbl_finishTABLE(me->stbl);
4947 CTRACE((tfp, "endStblTABLE: ncols = %d.\n", ncols));
4948
4949 if (ncols > 0) {
4950 lines_changed = HText_insertBlanksInStblLines(me, ncols);
4951 CTRACE((tfp, "endStblTABLE: changed %d lines, done.\n", lines_changed));
4952 #ifdef DISP_PARTIAL
4953 /* allow HTDisplayPartial() to redisplay the changed lines.
4954 * There is no harm if we got several stbl in the document, hope so.
4955 */
4956 NumOfLines_partial -= lines_changed; /* fake */
4957 #endif /* DISP_PARTIAL */
4958 }
4959 #ifdef EXP_NESTED_TABLES
4960 if (nested_tables) {
4961 enclosing = Stbl_get_enclosing(me->stbl);
4962 me->last_anchor_before_stbl = Stbl_get_last_anchor_before(me->stbl);
4963 if (enclosing == NULL) {
4964 Stbl_free(me->stbl);
4965 free_enclosed_stbl(me);
4966 } else {
4967 if (me->enclosed_stbl == NULL)
4968 me->enclosed_stbl = HTList_new();
4969 HTList_addObject(me->enclosed_stbl, me->stbl);
4970 CTRACE((tfp, "endStblTABLE: postpone free %p\n", (void *) me->stbl));
4971 }
4972 me->stbl = enclosing;
4973 } else {
4974 Stbl_free(me->stbl);
4975 me->stbl = NULL;
4976 }
4977 #else
4978 Stbl_free(me->stbl);
4979 me->stbl = NULL;
4980 #endif
4981
4982 CTRACE((tfp, "endStblTABLE: have%s enclosing table (%p)\n",
4983 enclosing == 0 ? " NO" : "", (void *) enclosing));
4984
4985 return (BOOLEAN) (enclosing != 0);
4986 }
4987
4988 /* Start simple table row
4989 */
HText_startStblTR(HText * me,int alignment)4990 void HText_startStblTR(HText *me, int alignment)
4991 {
4992 if (!me || !me->stbl)
4993 return;
4994 if (Stbl_addRowToTable(me->stbl, alignment, me->Lines) < 0) {
4995 HText_cancelStbl(me); /* give up */
4996 }
4997 }
4998
4999 /* Finish simple table row
5000 */
HText_endStblTR(HText * me)5001 void HText_endStblTR(HText *me)
5002 {
5003 if (!me || !me->stbl)
5004 return;
5005 /* should this do something?? */
5006 }
5007
5008 /* Start simple table cell
5009 */
HText_startStblTD(HText * me,int colspan,int rowspan,int alignment,int isheader)5010 void HText_startStblTD(HText *me, int colspan,
5011 int rowspan,
5012 int alignment,
5013 int isheader)
5014 {
5015 if (!me || !me->stbl)
5016 return;
5017 if (colspan < 0)
5018 colspan = 1;
5019 if (colspan > TRST_MAXCOLSPAN) {
5020 CTRACE((tfp, "*** COLSPAN=%d is too large, ignored!\n", colspan));
5021 colspan = 1;
5022 }
5023 if (rowspan > TRST_MAXROWSPAN) {
5024 CTRACE((tfp, "*** ROWSPAN=%d is too large, ignored!\n", rowspan));
5025 rowspan = 1;
5026 }
5027 if (Stbl_addCellToTable(me->stbl, colspan, rowspan, alignment, isheader,
5028 me->Lines,
5029 HText_LastLineOffset(me),
5030 HText_LastLineSize(me, FALSE)) < 0) {
5031 HText_cancelStbl(me); /* give up */
5032 }
5033 }
5034
5035 /* Finish simple table cell
5036 */
HText_endStblTD(HText * me)5037 void HText_endStblTD(HText *me)
5038 {
5039 if (!me || !me->stbl)
5040 return;
5041 if (Stbl_finishCellInTable(me->stbl, TRST_ENDCELL_ENDTD,
5042 me->Lines,
5043 HText_LastLineOffset(me),
5044 HText_LastLineSize(me, FALSE)) < 0) {
5045 HText_cancelStbl(me); /* give up */
5046 }
5047 }
5048
5049 /* Remember COL info / Start a COLGROUP and remember info
5050 */
HText_startStblCOL(HText * me,int span,int alignment,int isgroup)5051 void HText_startStblCOL(HText *me, int span,
5052 int alignment,
5053 int isgroup)
5054 {
5055 if (!me || !me->stbl)
5056 return;
5057 if (span <= 0)
5058 span = 1;
5059 if (span > TRST_MAXCOLSPAN) {
5060 CTRACE((tfp, "*** SPAN=%d is too large, ignored!\n", span));
5061 span = 1;
5062 }
5063 if (Stbl_addColInfo(me->stbl, span, alignment, isgroup) < 0) {
5064 HText_cancelStbl(me); /* give up */
5065 }
5066 }
5067
5068 /* Finish a COLGROUP
5069 */
HText_endStblCOLGROUP(HText * me)5070 void HText_endStblCOLGROUP(HText *me)
5071 {
5072 if (!me || !me->stbl)
5073 return;
5074 if (Stbl_finishColGroup(me->stbl) < 0) {
5075 HText_cancelStbl(me); /* give up */
5076 }
5077 }
5078
5079 /* Start a THEAD / TFOOT / TBODY - remember its alignment info
5080 */
HText_startStblRowGroup(HText * me,int alignment)5081 void HText_startStblRowGroup(HText *me, int alignment)
5082 {
5083 if (!me || !me->stbl)
5084 return;
5085 if (Stbl_addRowGroup(me->stbl, alignment) < 0) {
5086 HText_cancelStbl(me); /* give up */
5087 }
5088 }
5089
compute_show_number(TextAnchor * a)5090 static void compute_show_number(TextAnchor *a)
5091 {
5092 HTAnchor *cur, *tst;
5093 TextAnchor *b;
5094 int match;
5095
5096 a->show_number = a->number;
5097 if (unique_urls
5098 && HTMainText != 0
5099 && HTMainText->first_anchor != 0
5100 && a->anchor != 0
5101 && (cur = a->anchor->dest) != 0
5102 && cur->parent != 0
5103 && cur->parent->address != 0) {
5104
5105 match = 0;
5106 for (b = HTMainText->first_anchor; b != a; b = b->next) {
5107 if (b->anchor != 0
5108 && (tst = b->anchor->dest) != 0
5109 && tst->parent != 0
5110 && tst->parent->address != 0
5111 && !strcmp(cur->parent->address,
5112 tst->parent->address)
5113 && !strcmp(NonNull(a->anchor->tag), NonNull(b->anchor->tag))) {
5114 match = b->show_number;
5115 break;
5116 }
5117 }
5118 if (match)
5119 a->show_number = match;
5120 else
5121 a->show_number = HTMainText->next_number++;
5122 }
5123 }
5124
5125 /* Anchor handling
5126 * ---------------
5127 */
add_link_number(HText * text,TextAnchor * a,int save_position)5128 static void add_link_number(HText *text, TextAnchor *a, int save_position)
5129 {
5130 char marker[32];
5131
5132 /*
5133 * If we are doing link_numbering add the link number.
5134 */
5135 if ((a->number > 0)
5136 #ifdef USE_PRETTYSRC
5137 && (text->source ? !psrcview_no_anchor_numbering : 1)
5138 #endif
5139 && links_are_numbered()) {
5140 char saved_lastchar = text->LastChar;
5141 int saved_linenum = text->Lines;
5142 HTAnchor *link_dest;
5143 char *link_text;
5144
5145 compute_show_number(a);
5146
5147 if (dump_links_inline
5148 && (link_dest = HTAnchor_followLink(a->anchor)) != 0
5149 && (link_text = HTAnchor_address(link_dest)) != 0) {
5150 HText_appendText(text, "[");
5151 HText_appendText(text, link_text);
5152 HText_appendText(text, "]");
5153 } else {
5154 sprintf(marker, "[%d]", a->show_number);
5155 HText_appendText(text, marker);
5156 }
5157 if (saved_linenum && text->Lines && saved_lastchar != ' ')
5158 text->LastChar = ']'; /* if marker not after space caused split */
5159 if (save_position) {
5160 a->line_num = text->Lines;
5161 a->line_pos = (short) text->last_line->size;
5162 }
5163 }
5164 }
5165
5166 /* Start an anchor field
5167 */
HText_beginAnchor(HText * text,int underline,HTChildAnchor * anc)5168 int HText_beginAnchor(HText *text, int underline,
5169 HTChildAnchor *anc)
5170 {
5171 TextAnchor *a;
5172
5173 POOLtypecalloc(TextAnchor, a);
5174
5175 if (a == NULL)
5176 outofmem(__FILE__, "HText_beginAnchor");
5177
5178 a->inUnderline = (BOOLEAN) underline;
5179
5180 a->sgml_offset = SGML_offset();
5181 a->line_num = text->Lines;
5182 a->line_pos = (short) text->last_line->size;
5183 if (text->last_anchor) {
5184 text->last_anchor->next = a;
5185 } else {
5186 text->first_anchor = a;
5187 }
5188 a->next = 0;
5189 a->anchor = anc;
5190 a->extent = 0;
5191 a->link_type = HYPERTEXT_ANCHOR;
5192 text->last_anchor = a;
5193
5194 if (track_internal_links
5195 && HTAnchor_followTypedLink(anc, HTInternalLink)) {
5196 a->number = ++(text->last_anchor_number);
5197 a->link_type = INTERNAL_LINK_ANCHOR;
5198 } else if (HTAnchor_followLink(anc)) {
5199 a->number = ++(text->last_anchor_number);
5200 } else {
5201 a->number = 0;
5202 }
5203 a->show_number = 0;
5204
5205 if (number_links_on_left)
5206 add_link_number(text, a, TRUE);
5207 return (a->number);
5208 }
5209
5210 /* If !really, report whether the anchor is empty. */
HText_endAnchor0(HText * text,int number,int really)5211 static BOOL HText_endAnchor0(HText *text, int number,
5212 int really)
5213 {
5214 TextAnchor *a;
5215
5216 /*
5217 * The number argument is set to 0 in HTML.c and
5218 * LYCharUtils.c when we want to end the anchor
5219 * for the immediately preceding HText_beginAnchor()
5220 * call. If it's greater than 0, we want to handle
5221 * a particular anchor. This allows us to set links
5222 * for positions indicated by NAME or ID attributes,
5223 * without needing to close any anchor with an HREF
5224 * within which that link might be embedded. -FM
5225 */
5226 if (number <= 0 || number == text->last_anchor->number) {
5227 a = text->last_anchor;
5228 } else {
5229 for (a = text->first_anchor; a; a = a->next) {
5230 if (a->number == number) {
5231 break;
5232 }
5233 }
5234 if (a == NULL) {
5235 /*
5236 * There's no anchor with that number,
5237 * so we'll default to the last anchor,
5238 * and cross our fingers. -FM
5239 */
5240 a = text->last_anchor;
5241 }
5242 }
5243
5244 CTRACE((tfp, "GridText:HText_endAnchor0: number:%d link_type:%d\n",
5245 a->number, a->link_type));
5246 if (a->link_type == INPUT_ANCHOR) {
5247 /*
5248 * Shouldn't happen, but put test here anyway to be safe. - LE
5249 */
5250
5251 CTRACE((tfp,
5252 "BUG: HText_endAnchor0: internal error: last anchor was input field!\n"));
5253 return FALSE;
5254 }
5255
5256 if (a->number) {
5257 /*
5258 * If it goes somewhere...
5259 */
5260 int i, j, k, l;
5261 BOOL remove_numbers_on_empty = (BOOL) ((links_are_numbered() &&
5262 ((text->hiddenlinkflag != HIDDENLINKS_MERGE)
5263 || (LYNoISMAPifUSEMAP &&
5264 !(text->node_anchor && text->node_anchor->bookmark)
5265 && HTAnchor_isISMAPScript
5266 (HTAnchor_followLink(a->anchor))))));
5267 HTLine *last = text->last_line;
5268 HTLine *prev = text->last_line->prev;
5269 HTLine *start = last;
5270 int CurBlankExtent = 0;
5271 int BlankExtent = 0;
5272 int extent_adjust = 0;
5273
5274 /* Find the length taken by the anchor */
5275 l = text->Lines; /* lineno of last */
5276
5277 /* the last line of an anchor may contain a trailing blank,
5278 * which will be trimmed later. Discount it from the extent.
5279 */
5280 if (l > a->line_num) {
5281 for (i = start->size; i > 0; --i) {
5282 if (isspace(UCH(start->data[i - 1]))) {
5283 --extent_adjust;
5284 } else {
5285 break;
5286 }
5287 }
5288 }
5289
5290 while (l > a->line_num) {
5291 extent_adjust += start->size;
5292 start = start->prev;
5293 l--;
5294 }
5295 /* Now start is the start line of the anchor */
5296 extent_adjust += start->size - a->line_pos;
5297 start = last; /* Used later */
5298
5299 /*
5300 * Check if the anchor content has only
5301 * white and special characters, starting
5302 * with the content on the last line. -FM
5303 */
5304 a->extent = (short) (a->extent + extent_adjust);
5305 if (a->extent > (int) last->size) {
5306 /*
5307 * The anchor extends over more than one line,
5308 * so set up to check the entire last line. -FM
5309 */
5310 i = last->size;
5311 } else {
5312 /*
5313 * The anchor is restricted to the last line,
5314 * so check from the start of the anchor. -FM
5315 */
5316 i = a->extent;
5317 }
5318 k = j = (last->size - i);
5319 while (j < (int) last->size) {
5320 if (!IsSpecialAttrChar(last->data[j]) &&
5321 !isspace(UCH(last->data[j])) &&
5322 last->data[j] != HT_NON_BREAK_SPACE &&
5323 last->data[j] != HT_EN_SPACE)
5324 break;
5325 i--;
5326 j++;
5327 }
5328 if (i == 0) {
5329 if (a->extent > (int) last->size) {
5330 /*
5331 * The anchor starts on a preceding line, and
5332 * the last line has only white and special
5333 * characters, so declare the entire extent
5334 * of the last line as blank. -FM
5335 */
5336 CurBlankExtent = BlankExtent = last->size;
5337 } else {
5338 /*
5339 * The anchor starts on the last line, and
5340 * has only white or special characters, so
5341 * declare the anchor's extent as blank. -FM
5342 */
5343 CurBlankExtent = BlankExtent = a->extent;
5344 }
5345 }
5346 /*
5347 * While the anchor starts on a line preceding
5348 * the one we just checked, and the one we just
5349 * checked has only white and special characters,
5350 * check whether the anchor's content on the
5351 * immediately preceding line also has only
5352 * white and special characters. -FM
5353 */
5354 while (i == 0 &&
5355 (a->extent > CurBlankExtent ||
5356 (a->extent == CurBlankExtent &&
5357 k == 0 &&
5358 prev != text->last_line &&
5359 (prev->size == 0 ||
5360 prev->data[prev->size - 1] == ']')))) {
5361 start = prev;
5362 k = j = prev->size - a->extent + CurBlankExtent;
5363 if (j < 0) {
5364 /*
5365 * The anchor starts on a preceding line,
5366 * so check all of this line. -FM
5367 */
5368 j = 0;
5369 i = prev->size;
5370 } else {
5371 /*
5372 * The anchor starts on this line. -FM
5373 */
5374 i = a->extent - CurBlankExtent;
5375 }
5376 while (j < (int) prev->size) {
5377 if (!IsSpecialAttrChar(prev->data[j]) &&
5378 !isspace(UCH(prev->data[j])) &&
5379 prev->data[j] != HT_NON_BREAK_SPACE &&
5380 prev->data[j] != HT_EN_SPACE)
5381 break;
5382 i--;
5383 j++;
5384 }
5385 if (i == 0) {
5386 if (a->extent > (CurBlankExtent + (int) prev->size) ||
5387 (a->extent == CurBlankExtent + (int) prev->size &&
5388 k == 0 &&
5389 prev->prev != text->last_line &&
5390 (prev->prev->size == 0 ||
5391 prev->prev->data[prev->prev->size - 1] == ']'))) {
5392 /*
5393 * This line has only white and special
5394 * characters, so treat its entire extent
5395 * as blank, and decrement the pointer for
5396 * the line to be analyzed. -FM
5397 */
5398 CurBlankExtent += prev->size;
5399 BlankExtent = CurBlankExtent;
5400 prev = prev->prev;
5401 } else {
5402 /*
5403 * The anchor starts on this line, and it
5404 * has only white or special characters, so
5405 * declare the anchor's extent as blank. -FM
5406 */
5407 BlankExtent = a->extent;
5408 break;
5409 }
5410 }
5411 }
5412 if (!really) { /* Just report whether it is empty */
5413 a->extent = (short) (a->extent - extent_adjust);
5414 return (BOOL) (i == 0);
5415 }
5416 if (i == 0) {
5417 /*
5418 * It's an invisible anchor probably from an ALT=""
5419 * or an ignored ISMAP attribute due to a companion
5420 * USEMAP. -FM
5421 */
5422 a->show_anchor = NO;
5423
5424 CTRACE((tfp,
5425 "HText_endAnchor0: hidden (line,pos,ext,BlankExtent):(%d,%d,%d,%d)",
5426 a->line_num, a->line_pos, a->extent,
5427 BlankExtent));
5428
5429 /*
5430 * If links are numbered, then try to get rid of the
5431 * numbered bracket and adjust the anchor count. -FM
5432 *
5433 * Well, let's do this only if -hiddenlinks=merged is not in
5434 * effect, or if we can be reasonably sure that
5435 * this is the result of an intentional non-generation of
5436 * anchor text via NO_ISMAP_IF_USEMAP. In other cases it can
5437 * actually be a feature that numbered links alert the viewer
5438 * to the presence of a link which is otherwise not selectable -
5439 * possibly caused by HTML errors. - kw
5440 */
5441 if (remove_numbers_on_empty) {
5442 int NumSize = 0;
5443 TextAnchor *anc;
5444
5445 /*
5446 * Set start->data[j] to the close-square-bracket,
5447 * or to the beginning of the line on which the
5448 * anchor start. -FM
5449 */
5450 if (start == last) {
5451 /*
5452 * The anchor starts on the last line. -FM
5453 */
5454 j = (last->size - a->extent - 1);
5455 } else {
5456 /*
5457 * The anchor starts on a previous line. -FM
5458 */
5459 prev = start->prev;
5460 j = (start->size - a->extent + CurBlankExtent - 1);
5461 }
5462 if (j < 0)
5463 j = 0;
5464 i = j;
5465
5466 /*
5467 * If start->data[j] is a close-square-bracket, verify
5468 * that it's the end of the numbered bracket, and if so,
5469 * strip the numbered bracket. If start->data[j] is not
5470 * a close-square-bracket, check whether we had a wrap
5471 * and the close-square-bracket is at the end of the
5472 * previous line. If so, strip the numbered bracket
5473 * from that line. -FM
5474 */
5475 if (start->data[j] == ']') {
5476 j--;
5477 NumSize++;
5478 while (j >= 0 && isdigit(UCH(start->data[j]))) {
5479 j--;
5480 NumSize++;
5481 }
5482 while (j < 0) {
5483 j++;
5484 NumSize--;
5485 }
5486 if (start->data[j] == '[') {
5487 /*
5488 * The numbered bracket is entirely
5489 * on this line. -FM
5490 */
5491 NumSize++;
5492 if (start == last && (int) text->permissible_split > j) {
5493 if ((int) text->permissible_split - NumSize < j)
5494 text->permissible_split = (unsigned) j;
5495 else
5496 text->permissible_split -= (unsigned) NumSize;
5497 }
5498 k = j + NumSize;
5499 while (k < (int) start->size)
5500 start->data[j++] = start->data[k++];
5501 for (anc = a; anc; anc = anc->next) {
5502 if (anc->line_num == a->line_num &&
5503 anc->line_pos >= NumSize) {
5504 anc->line_pos = (short) (anc->line_pos - NumSize);
5505 }
5506 }
5507 start->size = (unsigned short) j;
5508 start->data[j++] = '\0';
5509 while (j < k)
5510 start->data[j++] = '\0';
5511 } else if (prev && prev->size > 1) {
5512 k = (i + 1);
5513 j = (prev->size - 1);
5514 while ((j >= 0) && IsSpecialAttrChar(prev->data[j]))
5515 j--;
5516 i = (j + 1);
5517 while (j >= 0 &&
5518 isdigit(UCH(prev->data[j]))) {
5519 j--;
5520 NumSize++;
5521 }
5522 while (j < 0) {
5523 j++;
5524 NumSize--;
5525 }
5526 if (prev->data[j] == '[') {
5527 /*
5528 * The numbered bracket started on the
5529 * previous line, and part of it was
5530 * wrapped to this line. -FM
5531 */
5532 while (i < (int) prev->size)
5533 prev->data[j++] = prev->data[i++];
5534 prev->size = (unsigned short) j;
5535 prev->data[j] = '\0';
5536 while (j < i)
5537 prev->data[j++] = '\0';
5538 if (start == last && text->permissible_split > 0) {
5539 if ((int) text->permissible_split < k)
5540 text->permissible_split = 0;
5541 else
5542 text->permissible_split -= (unsigned) k;
5543 }
5544 j = 0;
5545 i = k;
5546 while (k < (int) start->size)
5547 start->data[j++] = start->data[k++];
5548 for (anc = a; anc; anc = anc->next) {
5549 if (anc->line_num == a->line_num &&
5550 anc->line_pos >= i) {
5551 anc->line_pos = (short) (anc->line_pos - i);
5552 }
5553 }
5554 start->size = (unsigned short) j;
5555 start->data[j++] = '\0';
5556 while (j < k)
5557 start->data[j++] = '\0';
5558 } else {
5559 /*
5560 * Shucks! We didn't find the
5561 * numbered bracket. -FM
5562 */
5563 a->show_anchor = YES;
5564 }
5565 } else {
5566 /*
5567 * Shucks! We didn't find the
5568 * numbered bracket. -FM
5569 */
5570 a->show_anchor = YES;
5571 }
5572 } else if (prev && prev->size > 2) {
5573 j = (prev->size - 1);
5574 while ((j >= 0) && IsSpecialAttrChar(prev->data[j]))
5575 j--;
5576 if (j < 0)
5577 j = 0;
5578 if ((j >= 2) &&
5579 (prev->data[j] == ']' &&
5580 isdigit(UCH(prev->data[j - 1])))) {
5581 j--;
5582 NumSize++;
5583 while (j >= 0 &&
5584 isdigit(UCH(prev->data[j]))) {
5585 j--;
5586 NumSize++;
5587 }
5588 while (j < 0) {
5589 j++;
5590 NumSize--;
5591 }
5592 if (prev->data[j] == '[') {
5593 /*
5594 * The numbered bracket is all on the
5595 * previous line, and the anchor content
5596 * was wrapped to the last line. -FM
5597 */
5598 NumSize++;
5599 k = j + NumSize;
5600 while (k < (int) prev->size)
5601 prev->data[j++] = prev->data[k++];
5602 prev->size = (unsigned short) j;
5603 prev->data[j++] = '\0';
5604 while (j < k)
5605 prev->data[j++] = '\0';
5606 } else {
5607 /*
5608 * Shucks! We didn't find the
5609 * numbered bracket. -FM
5610 */
5611 a->show_anchor = YES;
5612 }
5613 } else {
5614 /*
5615 * Shucks! We didn't find the
5616 * numbered bracket. -FM
5617 */
5618 a->show_anchor = YES;
5619 }
5620 } else {
5621 /*
5622 * Shucks! We didn't find the
5623 * numbered bracket. -FM
5624 */
5625 a->show_anchor = YES;
5626 }
5627 }
5628 } else {
5629 if (!number_links_on_left)
5630 add_link_number(text, a, FALSE);
5631 /*
5632 * The anchor's content is not restricted to only
5633 * white and special characters, so we'll show it
5634 * as a link. -FM
5635 */
5636 a->show_anchor = YES;
5637 if (BlankExtent) {
5638 CTRACE((tfp,
5639 "HText_endAnchor0: blanks (line,pos,ext,BlankExtent):(%d,%d,%d,%d)",
5640 a->line_num, a->line_pos, a->extent,
5641 BlankExtent));
5642 }
5643 }
5644 if (a->show_anchor == NO) {
5645 /*
5646 * The anchor's content is restricted to white
5647 * and special characters, so set its number
5648 * and extent to zero, decrement the visible
5649 * anchor number counter, and add this anchor
5650 * to the hidden links list. -FM
5651 */
5652 a->extent = 0;
5653 if (text->hiddenlinkflag != HIDDENLINKS_MERGE) {
5654 a->number = 0;
5655 text->last_anchor_number--;
5656 HText_AddHiddenLink(text, a);
5657 }
5658 } else {
5659 /*
5660 * The anchor's content is not restricted to white
5661 * and special characters, so we'll display the
5662 * content, but shorten its extent by any trailing
5663 * blank lines we've detected. -FM
5664 */
5665 a->extent = (short) (a->extent - ((BlankExtent < a->extent)
5666 ? BlankExtent
5667 : 0));
5668 }
5669 if (BlankExtent || a->extent <= 0 || a->number <= 0) {
5670 CTRACE((tfp,
5671 "->[%d](%d,%d,%d,%d)\n",
5672 a->number,
5673 a->line_num, a->line_pos, a->extent,
5674 BlankExtent));
5675 }
5676 } else {
5677 if (!really) /* Just report whether it is empty */
5678 return FALSE;
5679 /*
5680 * It's a named anchor without an HREF, so it
5681 * should be registered but not shown as a
5682 * link. -FM
5683 */
5684 a->show_anchor = NO;
5685 a->extent = 0;
5686 }
5687 return FALSE;
5688 }
5689
HText_endAnchor(HText * text,int number)5690 void HText_endAnchor(HText *text, int number)
5691 {
5692 HText_endAnchor0(text, number, 1);
5693 }
5694
5695 /*
5696 This returns whether the given anchor has blank content. Shamelessly copied
5697 from HText_endAnchor. The values returned are meaningful only for "normal"
5698 links - like ones produced by <a href=".">foo</a>, no inputs, etc. - VH
5699 */
5700 #ifdef MARK_HIDDEN_LINKS
HText_isAnchorBlank(HText * text,int number)5701 BOOL HText_isAnchorBlank(HText *text, int number)
5702 {
5703 return HText_endAnchor0(text, number, 0);
5704 }
5705 #endif /* MARK_HIDDEN_LINKS */
5706
HText_appendText(HText * text,const char * str)5707 void HText_appendText(HText *text, const char *str)
5708 {
5709 const char *p;
5710
5711 if (str != NULL &&
5712 text != NULL &&
5713 text->halted != 3) {
5714 for (p = str; *p; p++) {
5715 HText_appendCharacter(text, *p);
5716 }
5717 }
5718 }
5719
remove_special_attr_chars(char * buf)5720 static int remove_special_attr_chars(char *buf)
5721 {
5722 register char *cp;
5723 register int soft_newline_count = 0;
5724
5725 for (cp = buf; *cp != '\0'; cp++) {
5726 /*
5727 * Don't print underline chars.
5728 */
5729 soft_newline_count += (*cp == LY_SOFT_NEWLINE);
5730 if (!IsSpecialAttrChar(*cp)) {
5731 *buf++ = *cp;
5732 }
5733 }
5734 *buf = '\0';
5735 return soft_newline_count;
5736 }
5737
5738 /*
5739 * This function trims blank lines from the end of the document, and
5740 * then gets the hightext from the text by finding the char position,
5741 * and brings the anchors in line with the text by adding the text
5742 * offset to each of the anchors.
5743 */
HText_endAppend(HText * text)5744 void HText_endAppend(HText *text)
5745 {
5746 HTLine *line_ptr;
5747
5748 if (!text)
5749 return;
5750
5751 CTRACE((tfp, "GridText: Entering HText_endAppend\n"));
5752
5753 /*
5754 * Create a blank line at the bottom.
5755 */
5756 new_line(text);
5757
5758 if (text->halted) {
5759 if (text->stbl) {
5760 HText_cancelStbl(text);
5761 }
5762 /*
5763 * If output was stopped because memory was low, and we made
5764 * it to the end of the document, reset those flags and hope
5765 * things are better now. - kw
5766 */
5767 LYFakeZap(NO);
5768 text->halted = 0;
5769 } else if (text->stbl) {
5770 /*
5771 * Could happen if TABLE end tag was missing.
5772 * Alternatively we could cancel in this case. - kw
5773 */
5774 HText_endStblTABLE(text);
5775 }
5776
5777 /*
5778 * Get the first line.
5779 */
5780 if (LYtrimBlankLines && (line_ptr = FirstHTLine(text)) != 0) {
5781 /*
5782 * Remove blank lines at the end of the document.
5783 */
5784 while (text->last_line->data[0] == '\0' && text->Lines > 0) {
5785 HTLine *next_to_the_last_line = text->last_line->prev;
5786
5787 CTRACE((tfp, "GridText: Removing bottom blank line: `%s'\n",
5788 text->last_line->data));
5789 /*
5790 * line_ptr points to the first line.
5791 */
5792 next_to_the_last_line->next = line_ptr;
5793 line_ptr->prev = next_to_the_last_line;
5794 freeHTLine(text, text->last_line);
5795 text->last_line = next_to_the_last_line;
5796 text->Lines--;
5797 CTRACE((tfp, "GridText: New bottom line: `%s'\n",
5798 text->last_line->data));
5799 }
5800 }
5801
5802 /*
5803 * Fix up the anchor structure values and
5804 * create the hightext strings. -FM
5805 */
5806 HText_trimHightext(text, TRUE, -1);
5807 }
5808
5809 /*
5810 * This function gets the hightext from the text by finding the char
5811 * position, and brings the anchors in line with the text by adding the text
5812 * offset to each of the anchors.
5813 *
5814 * `Forms input' fields cannot be displayed properly without this function
5815 * to be invoked (detected in display_partial mode).
5816 *
5817 * If final is set, this is the final fixup; if not set, we don't have
5818 * to do everything because there should be another call later.
5819 *
5820 * BEFORE this function has treated a TextAnchor, its line_pos and
5821 * extent fields are counting bytes in the HTLine data, including
5822 * invisible special attribute chars and counting UTF-8 multibyte
5823 * characters as multiple bytes.
5824 *
5825 * AFTER the adjustment, the anchor line_pos (and hightext offset if
5826 * applicable) fields indicate x positions in terms of displayed character
5827 * cells, and the extent field apparently is unimportant; the anchor text has
5828 * been copied to the hightext fields (which should have been NULL up to that
5829 * point), with special attribute chars removed.
5830 *
5831 * This needs to be done so that display_page finds the anchors in the
5832 * form it expects when it sets the links[] elements.
5833 */
HText_trimHightext(HText * text,int final,int stop_before)5834 static void HText_trimHightext(HText *text,
5835 int final,
5836 int stop_before)
5837 {
5838 int cur_line, cur_shift;
5839 TextAnchor *anchor_ptr;
5840 TextAnchor *prev_a = NULL;
5841 HTLine *line_ptr;
5842 HTLine *line_ptr2;
5843 unsigned char ch;
5844 char *hilite_str;
5845 int hilite_len;
5846 int actual_len;
5847 int count_line;
5848
5849 if (!text)
5850 return;
5851
5852 if (final) {
5853 CTRACE((tfp, "GridText: Entering HText_trimHightext (final)\n"));
5854 } else {
5855 if (stop_before < 0 || stop_before > text->Lines)
5856 stop_before = text->Lines;
5857 CTRACE((tfp,
5858 "GridText: Entering HText_trimHightext (partial: 0..%d/%d)\n",
5859 stop_before, text->Lines));
5860 }
5861
5862 /*
5863 * Get the first line.
5864 */
5865 line_ptr = FirstHTLine(text);
5866 cur_line = 0;
5867
5868 /*
5869 * Fix up the anchor structure values and
5870 * create the hightext strings. -FM
5871 */
5872 for (anchor_ptr = text->first_anchor;
5873 anchor_ptr != NULL;
5874 prev_a = anchor_ptr, anchor_ptr = anchor_ptr->next) {
5875 int anchor_col;
5876
5877 re_parse:
5878 /*
5879 * Find the right line.
5880 */
5881 for (; anchor_ptr->line_num > cur_line;
5882 line_ptr = line_ptr->next, cur_line++) {
5883 ; /* null body */
5884 }
5885
5886 if (!final) {
5887 /*
5888 * If this is not the final call, stop when we have reached
5889 * the last line, or the very end of preceding line.
5890 * The last line is probably still not finished. - kw
5891 */
5892 if (cur_line >= stop_before)
5893 break;
5894 if (anchor_ptr->line_num >= text->Lines - 1
5895 && anchor_ptr->line_pos >= (int) text->last_line->prev->size)
5896 break;
5897 /*
5898 * Also skip this anchor if it looks like HText_endAnchor
5899 * is not yet done with it. - kw
5900 */
5901 if (!anchor_ptr->extent && anchor_ptr->number &&
5902 (anchor_ptr->link_type & HYPERTEXT_ANCHOR) &&
5903 !anchor_ptr->show_anchor &&
5904 anchor_ptr->number == text->last_anchor_number)
5905 continue;
5906 }
5907
5908 /*
5909 * If hightext has already been set, then we must have already
5910 * done the trimming & adjusting for this anchor, so avoid
5911 * doing it a second time. - kw
5912 */
5913 if (LYGetHiTextStr(anchor_ptr, 0) != NULL)
5914 continue;
5915
5916 if (anchor_ptr->line_pos > (int) line_ptr->size) {
5917 anchor_ptr->line_pos = (short) line_ptr->size;
5918 }
5919 if (anchor_ptr->line_pos < 0) {
5920 anchor_ptr->line_pos = 0;
5921 anchor_ptr->line_num = cur_line;
5922 }
5923 CTRACE((tfp,
5924 "GridText: Anchor found on line:%d col:%d [%05d:%d] ext:%d\n",
5925 cur_line,
5926 anchor_ptr->line_pos,
5927 anchor_ptr->sgml_offset,
5928 anchor_ptr->number,
5929 anchor_ptr->extent));
5930
5931 cur_shift = 0;
5932 /*
5933 * Strip off any spaces or SpecialAttrChars at the beginning,
5934 * if they exist, but only on HYPERTEXT_ANCHORS.
5935 */
5936 if (anchor_ptr->link_type & HYPERTEXT_ANCHOR) {
5937 ch = UCH(line_ptr->data[anchor_ptr->line_pos]);
5938 while (isspace(ch) ||
5939 IsSpecialAttrChar(ch)) {
5940 anchor_ptr->line_pos++;
5941 anchor_ptr->extent--;
5942 cur_shift++;
5943 ch = UCH(line_ptr->data[anchor_ptr->line_pos]);
5944 }
5945 }
5946 if (anchor_ptr->extent < 0) {
5947 anchor_ptr->extent = 0;
5948 }
5949
5950 CTRACE((tfp, "anchor text: '%s'\n", line_ptr->data));
5951 /*
5952 * If the link begins with an end of line and we have more lines, then
5953 * start the highlighting on the next line. -FM.
5954 *
5955 * But if an empty anchor is at the end of line and empty, keep it
5956 * where it is, unless the previous anchor in the list (if any) already
5957 * starts later. - kw
5958 */
5959 if ((unsigned) anchor_ptr->line_pos >= strlen(line_ptr->data)) {
5960 if (cur_line < text->Lines &&
5961 (anchor_ptr->extent ||
5962 anchor_ptr->line_pos != (int) line_ptr->size ||
5963 (prev_a && /* How could this happen? */
5964 (prev_a->line_num > anchor_ptr->line_num)))) {
5965 anchor_ptr->line_num++;
5966 anchor_ptr->line_pos = 0;
5967 CTRACE((tfp, "found anchor at end of line\n"));
5968 goto re_parse;
5969 } else {
5970 CTRACE((tfp, "found anchor at end of line, leaving it there\n"));
5971 }
5972 }
5973
5974 /*
5975 * Copy the link name into the data structure.
5976 */
5977 if (anchor_ptr->extent > 0
5978 && anchor_ptr->line_pos >= 0) {
5979 int size = (int) line_ptr->size - anchor_ptr->line_pos;
5980
5981 if (size > anchor_ptr->extent)
5982 size = anchor_ptr->extent;
5983 LYClearHiText(anchor_ptr);
5984 LYSetHiText(anchor_ptr,
5985 &line_ptr->data[anchor_ptr->line_pos],
5986 (unsigned) size);
5987 } else {
5988 LYClearHiText(anchor_ptr);
5989 LYSetHiText(anchor_ptr, "", 0);
5990 }
5991
5992 /*
5993 * If the anchor extends over more than one line, copy that into the
5994 * data structure.
5995 */
5996 hilite_str = LYGetHiTextStr(anchor_ptr, 0);
5997 hilite_len = (int) strlen(hilite_str);
5998 actual_len = anchor_ptr->extent;
5999
6000 line_ptr2 = line_ptr;
6001 assert(line_ptr2 != 0);
6002
6003 count_line = cur_line;
6004 while (actual_len > hilite_len) {
6005 HTLine *old_line_ptr2 = line_ptr2;
6006
6007 count_line++;
6008 if ((line_ptr2 = line_ptr2->next) == NULL)
6009 break;
6010
6011 if (!final
6012 && count_line >= stop_before) {
6013 LYClearHiText(anchor_ptr);
6014 break;
6015 } else if (old_line_ptr2 == text->last_line) {
6016 break;
6017 }
6018
6019 /*
6020 * Double check that we have a line pointer, and if so, copy into
6021 * highlight text.
6022 */
6023 if (line_ptr2) {
6024 char *hi_string = NULL;
6025 int hi_offset = line_ptr2->offset;
6026
6027 StrnAllocCopy(hi_string,
6028 line_ptr2->data,
6029 (size_t) (actual_len - hilite_len));
6030 actual_len -= (int) strlen(hi_string);
6031 /*handle LY_SOFT_NEWLINEs -VH */
6032 hi_offset += remove_special_attr_chars(hi_string);
6033
6034 if (anchor_ptr->link_type & HYPERTEXT_ANCHOR) {
6035 LYTrimTrailing(hi_string);
6036 }
6037 if (non_empty(hi_string)) {
6038 LYAddHiText(anchor_ptr, hi_string, hi_offset);
6039 } else if (actual_len > hilite_len) {
6040 LYAddHiText(anchor_ptr, "", hi_offset);
6041 }
6042 FREE(hi_string);
6043 }
6044 }
6045
6046 if (!final
6047 && count_line >= stop_before) {
6048 break;
6049 }
6050
6051 hilite_str = LYGetHiTextStr(anchor_ptr, 0);
6052 remove_special_attr_chars(hilite_str);
6053 if (anchor_ptr->link_type & HYPERTEXT_ANCHOR) {
6054 LYTrimTrailing(hilite_str);
6055 }
6056
6057 /*
6058 * Save the offset (bytes) of the anchor in the line's data.
6059 */
6060 anchor_col = anchor_ptr->line_pos;
6061
6062 /*
6063 * Subtract any formatting characters from the x position of the link.
6064 */
6065 #ifdef WIDEC_CURSES
6066 if (anchor_ptr->line_pos > 0) {
6067 /*
6068 * LYstrExtent filters out the formatting characters, so we do not
6069 * have to count them here, except for soft newlines.
6070 */
6071 anchor_ptr->line_pos = (short) LYstrExtent2(line_ptr->data, anchor_col);
6072 if (line_ptr->data[0] == LY_SOFT_NEWLINE)
6073 anchor_ptr->line_pos = (short) (anchor_ptr->line_pos + 1);
6074 }
6075 #else /* 8-bit curses, etc. */
6076 if (anchor_ptr->line_pos > 0) {
6077 register int offset = 0, i = 0;
6078 int have_soft_newline_in_1st_line = 0;
6079
6080 for (; i < anchor_col; i++) {
6081 if (IS_UTF_EXTRA(line_ptr->data[i]) ||
6082 IsSpecialAttrChar(line_ptr->data[i])) {
6083 offset++;
6084 have_soft_newline_in_1st_line += (line_ptr->data[i] == LY_SOFT_NEWLINE);
6085 }
6086 }
6087 anchor_ptr->line_pos = (short) (anchor_ptr->line_pos - offset);
6088 /*handle LY_SOFT_NEWLINEs -VH */
6089 anchor_ptr->line_pos = (short) (anchor_ptr->line_pos + have_soft_newline_in_1st_line);
6090 }
6091 #endif /* WIDEC_CURSES */
6092
6093 /*
6094 * Set the line number.
6095 */
6096 anchor_ptr->line_pos = (short) (anchor_ptr->line_pos + line_ptr->offset);
6097 anchor_ptr->line_num = cur_line;
6098
6099 CTRACE((tfp, "GridText: add link on line %d col %d [%d] %s\n",
6100 cur_line, anchor_ptr->line_pos,
6101 anchor_ptr->number, "in HText_trimHightext"));
6102 }
6103 }
6104
6105 /* Return the anchor associated with this node
6106 */
HText_nodeAnchor(HText * text)6107 HTParentAnchor *HText_nodeAnchor(HText *text)
6108 {
6109 return text->node_anchor;
6110 }
6111
6112 /* GridText specials
6113 * =================
6114 */
6115
6116 /*
6117 * HText_childNextNumber() returns the anchor with index [number],
6118 * using a pointer from the previous number (=optimization) or NULL.
6119 */
HText_childNextNumber(int number,void ** prev)6120 HTChildAnchor *HText_childNextNumber(int number, void **prev)
6121 {
6122 /* Sorry, TextAnchor is not declared outside this file, use a cast. */
6123 TextAnchor *a = (TextAnchor *) *prev;
6124
6125 if (!HTMainText || number <= 0)
6126 return (HTChildAnchor *) 0; /* Fail */
6127 if (number == 1 || !a)
6128 a = HTMainText->first_anchor;
6129
6130 /* a strange thing: positive a->number's are sorted,
6131 * and between them several a->number's may be 0 -- skip them
6132 */
6133 for (; a && a->number != number; a = a->next) ;
6134
6135 if (!a)
6136 return (HTChildAnchor *) 0; /* Fail */
6137 *prev = (void *) a;
6138 return a->anchor;
6139 }
6140
6141 /*
6142 * For the -unique-urls option, find the anchor-number of the first occurrence
6143 * of a given address.
6144 */
HText_findAnchorNumber(void * avoid)6145 int HText_findAnchorNumber(void *avoid)
6146 {
6147 TextAnchor *a = (TextAnchor *) avoid;
6148
6149 if (a->number > 0 && a->show_number == 0)
6150 compute_show_number(a);
6151
6152 return a->show_number;
6153 }
6154
inputFieldDesc(FormInfo * input)6155 static const char *inputFieldDesc(FormInfo * input)
6156 {
6157 const char *result = 0;
6158
6159 switch (input->type) {
6160 case F_TEXT_TYPE:
6161 result = gettext("text entry field");
6162 break;
6163 case F_PASSWORD_TYPE:
6164 result = gettext("password entry field");
6165 break;
6166 case F_CHECKBOX_TYPE:
6167 result = gettext("checkbox");
6168 break;
6169 case F_RADIO_TYPE:
6170 result = gettext("radio button");
6171 break;
6172 case F_SUBMIT_TYPE:
6173 result = gettext("submit button");
6174 break;
6175 case F_RESET_TYPE:
6176 result = gettext("reset button");
6177 break;
6178 case F_BUTTON_TYPE:
6179 result = gettext("script button");
6180 break;
6181 case F_OPTION_LIST_TYPE:
6182 result = gettext("popup menu");
6183 break;
6184 case F_HIDDEN_TYPE:
6185 result = gettext("hidden form field");
6186 break;
6187 case F_TEXTAREA_TYPE:
6188 result = gettext("text entry area");
6189 break;
6190 case F_RANGE_TYPE:
6191 result = gettext("range entry field");
6192 break;
6193 case F_FILE_TYPE:
6194 result = gettext("file entry field");
6195 break;
6196 case F_TEXT_SUBMIT_TYPE:
6197 result = gettext("text-submit field");
6198 break;
6199 case F_IMAGE_SUBMIT_TYPE:
6200 result = gettext("image-submit button");
6201 break;
6202 case F_KEYGEN_TYPE:
6203 result = gettext("keygen field");
6204 break;
6205 default:
6206 result = gettext("unknown form field");
6207 break;
6208 }
6209 return result;
6210 }
6211
6212 /*
6213 * HText_FormDescNumber() returns a description of the form field
6214 * with index N. The index corresponds to the [number] we print
6215 * for the field. -FM & LE
6216 */
HText_FormDescNumber(int number,const char ** desc)6217 void HText_FormDescNumber(int number,
6218 const char **desc)
6219 {
6220 TextAnchor *a;
6221
6222 if (!desc)
6223 return;
6224
6225 if (!(HTMainText && HTMainText->first_anchor) || number <= 0) {
6226 *desc = gettext("unknown field or link");
6227 return;
6228 }
6229
6230 for (a = HTMainText->first_anchor; a; a = a->next) {
6231 if (a->number == number) {
6232 if (!(a->input_field && a->input_field->type)) {
6233 *desc = gettext("unknown field or link");
6234 return;
6235 }
6236 break;
6237 }
6238 }
6239
6240 if (a != NULL)
6241 *desc = inputFieldDesc(a->input_field);
6242 }
6243
6244 /* HTGetRelLinkNum returns the anchor number to which follow_link_number()
6245 * is to jump (input was 123+ or 123- or 123+g or 123-g or 123 or 123g)
6246 * num is the number specified
6247 * rel is 0 or '+' or '-'
6248 * cur is the current link
6249 */
HTGetRelLinkNum(int num,int rel,int cur)6250 int HTGetRelLinkNum(int num,
6251 int rel,
6252 int cur)
6253 {
6254 TextAnchor *a, *l = 0;
6255 int scrtop = HText_getTopOfScreen(); /*XXX +1? */
6256 int curline = links[cur].anchor_line_num;
6257 int curpos = links[cur].lx;
6258 int on_screen = (curline >= scrtop && curline < (scrtop + display_lines));
6259
6260 /* curanchor may or may not be the "current link", depending whether it's
6261 * on the current screen
6262 */
6263 int curanchor = links[cur].anchor_number;
6264
6265 CTRACE((tfp, "HTGetRelLinkNum(%d,%d,%d) -- HTMainText=%p\n",
6266 num, rel, cur, (void *) HTMainText));
6267 CTRACE((tfp,
6268 " scrtop=%d, curline=%d, curanchor=%d, display_lines=%d, %s\n",
6269 scrtop, curline, curanchor, display_lines,
6270 on_screen ? "on_screen" : "0"));
6271 if (!HTMainText)
6272 return 0;
6273 if (rel == 0)
6274 return num;
6275
6276 /* if cur numbered link is on current page, use it */
6277 if (on_screen && curanchor) {
6278 CTRACE((tfp, "curanchor=%d at line %d on screen\n", curanchor, curline));
6279 if (rel == '+')
6280 return curanchor + num;
6281 else if (rel == '-')
6282 return curanchor - num;
6283 else
6284 return num; /* shouldn't happen */
6285 }
6286
6287 /* no current link on screen, or current link is not numbered
6288 * -- find previous closest numbered link
6289 */
6290 for (a = HTMainText->first_anchor; a; a = a->next) {
6291 CTRACE((tfp, " a->line_num=%d, a->number=%d\n", a->line_num, a->number));
6292 if (a->line_num >= scrtop)
6293 break;
6294 if (a->number == 0)
6295 continue;
6296 l = a;
6297 curanchor = l->number;
6298 }
6299 CTRACE((tfp, " a=%p, l=%p, curanchor=%d\n", (void *) a, (void *) l, curanchor));
6300 if (on_screen) { /* on screen but not a numbered link */
6301 for (; a; a = a->next) {
6302 if (a->number) {
6303 l = a;
6304 curanchor = l->number;
6305 }
6306 if (curline == a->line_num && curpos == a->line_pos)
6307 break;
6308 }
6309 }
6310 if (rel == '+') {
6311 return curanchor + num;
6312 } else if (rel == '-') {
6313 if (l)
6314 return curanchor + 1 - num;
6315 else {
6316 for (; a && a->number == 0; a = a->next) ;
6317 return a ? a->number - num : 0;
6318 }
6319 } else
6320 return num; /* shouldn't happen */
6321 }
6322
6323 /*
6324 * HTGetLinkInfo returns some link info based on the number.
6325 *
6326 * If want_go is not 0, caller requests to know a line number for
6327 * the link indicated by number. It will be returned in *go_line, and
6328 * *linknum will be set to an index into the links[] array, to use after
6329 * the line in *go_line has been made the new top screen line.
6330 * *hightext and *lname are unchanged. - KW
6331 *
6332 * If want_go is 0 and the number doesn't represent an input field, info
6333 * on the link indicated by number is deposited in *hightext and *lname.
6334 */
HTGetLinkInfo(int number,int want_go,int * go_line,int * linknum,char ** hightext,char ** lname)6335 int HTGetLinkInfo(int number,
6336 int want_go,
6337 int *go_line,
6338 int *linknum,
6339 char **hightext,
6340 char **lname)
6341 {
6342 TextAnchor *a;
6343 HTAnchor *link_dest;
6344
6345 HTAnchor *link_dest_intl = NULL;
6346 int anchors_this_line = 0, anchors_this_screen = 0;
6347 int prev_anchor_line = -1, prev_prev_anchor_line = -1;
6348
6349 if (!HTMainText)
6350 return (NO);
6351
6352 for (a = HTMainText->first_anchor; a; a = a->next) {
6353 /*
6354 * Count anchors, first on current line if there is more
6355 * than one. We have to count all links, including form
6356 * field anchors and others with a->number == 0, because
6357 * they are or will be included in the links[] array.
6358 * The exceptions are hidden form fields and anchors with
6359 * show_anchor not set, because they won't appear in links[]
6360 * and don't count towards nlinks. - KW
6361 */
6362 if ((a->show_anchor) &&
6363 !(a->link_type == INPUT_ANCHOR
6364 && a->input_field->type == F_HIDDEN_TYPE)) {
6365 if (a->line_num == prev_anchor_line) {
6366 anchors_this_line++;
6367 } else {
6368 /*
6369 * This anchor is on a different line than the previous one.
6370 * Remember which was the line number of the previous anchor,
6371 * for use in screen positioning later. - KW
6372 */
6373 anchors_this_line = 1;
6374 prev_prev_anchor_line = prev_anchor_line;
6375 prev_anchor_line = a->line_num;
6376 }
6377 if (a->line_num >= HTMainText->top_of_screen) {
6378 /*
6379 * Count all anchors starting with the top line of the
6380 * currently displayed screen. Just keep on counting
6381 * beyond this screen's bottom line - we'll know whether
6382 * a found anchor is below the current screen by a check
6383 * against nlinks later. - KW
6384 */
6385 anchors_this_screen++;
6386 }
6387 }
6388
6389 if (a->number == number) {
6390 /*
6391 * We found it. Now process it, depending
6392 * on what kind of info is requested. - KW
6393 */
6394 if (want_go || a->link_type == INPUT_ANCHOR) {
6395 if (a->show_anchor == NO) {
6396 /*
6397 * The number requested has been assigned to an anchor
6398 * without any selectable text, so we cannot position
6399 * on it. The code for suppressing such anchors in
6400 * HText_endAnchor() may not have applied, or it may
6401 * have failed. Return a failure indication so that
6402 * the user will notice that something is wrong,
6403 * instead of positioning on some other anchor which
6404 * might result in inadvertent activation. - KW
6405 */
6406 return (NO);
6407 }
6408 if (anchors_this_screen > 0 &&
6409 anchors_this_screen <= nlinks &&
6410 a->line_num >= HTMainText->top_of_screen &&
6411 a->line_num < HTMainText->top_of_screen + (display_lines)) {
6412 /*
6413 * If the requested anchor is within the current screen,
6414 * just set *go_line so that the screen window won't move
6415 * (keep it as it is), and set *linknum to the index of
6416 * this link in the current links[] array. - KW
6417 */
6418 *go_line = HTMainText->top_of_screen;
6419 if (linknum)
6420 *linknum = anchors_this_screen - 1;
6421 } else {
6422 /*
6423 * if the requested anchor is not within the currently
6424 * displayed screen, set *go_line such that the top line
6425 * will be either
6426 * (1) the line immediately below the previous
6427 * anchor, or
6428 * (2) about one third of a screenful above the line
6429 * with the target, or
6430 * (3) the first line of the document -
6431 * whichever comes last. In all cases the line with our
6432 * target will end up being the first line with any links
6433 * on the new screen, so that we can use the
6434 * anchors_this_line counter to point to the anchor in
6435 * the new links[] array. - kw
6436 */
6437 int max_offset = SEARCH_GOAL_LINE - 1;
6438
6439 if (max_offset < 0)
6440 max_offset = 0;
6441 else if (max_offset >= display_lines)
6442 max_offset = display_lines - 1;
6443 *go_line = prev_anchor_line - max_offset;
6444 if (*go_line <= prev_prev_anchor_line)
6445 *go_line = prev_prev_anchor_line + 1;
6446 if (*go_line < 0)
6447 *go_line = 0;
6448 if (linknum)
6449 *linknum = anchors_this_line - 1;
6450 }
6451 return (LINK_LINE_FOUND);
6452 } else {
6453 *hightext = LYGetHiTextStr(a, 0);
6454 link_dest = HTAnchor_followLink(a->anchor);
6455 {
6456 char *cp_freeme = NULL;
6457
6458 if (traversal) {
6459 cp_freeme = stub_HTAnchor_address(link_dest);
6460 } else if (track_internal_links) {
6461 if (a->link_type == INTERNAL_LINK_ANCHOR) {
6462 link_dest_intl =
6463 HTAnchor_followTypedLink(a->anchor, HTInternalLink);
6464 if (link_dest_intl && link_dest_intl != link_dest) {
6465
6466 CTRACE((tfp,
6467 "HTGetLinkInfo: unexpected typed link to %s!\n",
6468 link_dest_intl->parent->address));
6469 link_dest_intl = NULL;
6470 }
6471 }
6472 if (link_dest_intl) {
6473 char *cp2 = HTAnchor_address(link_dest_intl);
6474
6475 FREE(*lname);
6476 *lname = cp2;
6477 return (WWW_INTERN_LINK_TYPE);
6478 } else {
6479 cp_freeme = HTAnchor_address(link_dest);
6480 }
6481 } else {
6482 cp_freeme = HTAnchor_address(link_dest);
6483 }
6484 StrAllocCopy(*lname, cp_freeme);
6485 FREE(cp_freeme);
6486 }
6487 return (WWW_LINK_TYPE);
6488 }
6489 }
6490 }
6491 return (NO);
6492 }
6493
same_anchor_or_field(int numberA,FormInfo * formA,int numberB,FormInfo * formB,int ta_same)6494 static BOOLEAN same_anchor_or_field(int numberA,
6495 FormInfo * formA,
6496 int numberB,
6497 FormInfo * formB,
6498 int ta_same)
6499 {
6500 if (numberA > 0 || numberB > 0) {
6501 if (numberA == numberB)
6502 return (YES);
6503 else if (!ta_same)
6504 return (NO);
6505 }
6506 if (formA || formB) {
6507 if (formA == formB) {
6508 return (YES);
6509 } else if (!ta_same) {
6510 return (NO);
6511 } else if (!(formA && formB)) {
6512 return (NO);
6513 }
6514 } else {
6515 return (NO);
6516 }
6517 if (formA->type != formB->type ||
6518 formA->type != F_TEXTAREA_TYPE ||
6519 formB->type != F_TEXTAREA_TYPE) {
6520 return (NO);
6521 }
6522 if (formA->number != formB->number)
6523 return (NO);
6524 if (!formA->name || !formB->name)
6525 return (YES);
6526 return (BOOL) (strcmp(formA->name, formB->name) == 0);
6527 }
6528
6529 #define same_anchor_as_link(i,a,ta_same) (BOOL) (i >= 0 && a && \
6530 same_anchor_or_field(links[i].anchor_number,\
6531 (links[i].type == WWW_FORM_LINK_TYPE) ? links[i].l_form : NULL,\
6532 a->number,\
6533 (a->link_type == INPUT_ANCHOR) ? a->input_field : NULL,\
6534 ta_same))
6535 #define same_anchors(a1,a2,ta_same) (BOOL) (a1 && a2 && \
6536 same_anchor_or_field(a1->number,\
6537 (a1->link_type == INPUT_ANCHOR) ? a1->input_field : NULL,\
6538 a2->number,\
6539 (a2->link_type == INPUT_ANCHOR) ? a2->input_field : NULL,\
6540 ta_same))
6541
6542 /*
6543 * Are there more textarea lines belonging to the same textarea before
6544 * (direction < 0) or after (direction > 0) the current one?
6545 * On entry, curlink must be the index in links[] of a textarea field. - kw
6546 */
HText_TAHasMoreLines(int curlink,int direction)6547 BOOL HText_TAHasMoreLines(int curlink,
6548 int direction)
6549 {
6550 TextAnchor *a;
6551 TextAnchor *prev_a = NULL;
6552
6553 if (!HTMainText)
6554 return (NO);
6555 if (direction < 0) {
6556 for (a = HTMainText->first_anchor; a; prev_a = a, a = a->next) {
6557 if (a->link_type == INPUT_ANCHOR &&
6558 links[curlink].l_form == a->input_field) {
6559 return same_anchors(a, prev_a, TRUE);
6560 }
6561 if (links[curlink].anchor_number &&
6562 a->number >= links[curlink].anchor_number)
6563 break;
6564 }
6565 return NO;
6566 } else {
6567 for (a = HTMainText->first_anchor; a; a = a->next) {
6568 if (a->link_type == INPUT_ANCHOR &&
6569 links[curlink].l_form == a->input_field) {
6570 return same_anchors(a, a->next, TRUE);
6571 }
6572 if (links[curlink].anchor_number &&
6573 a->number >= links[curlink].anchor_number)
6574 break;
6575 }
6576 return NO;
6577 }
6578 }
6579
6580 /*
6581 * HTGetLinkOrFieldStart - moving to previous or next link or form field.
6582 *
6583 * On input,
6584 * curlink: current link, as index in links[] array (-1 if none)
6585 * direction: whether to move up or down (or stay where we are)
6586 * ta_skip: if FALSE, input fields belonging to the same textarea are
6587 * are treated as different fields, as usual;
6588 * if TRUE, fields of the same textarea are treated as a
6589 * group for skipping.
6590 * The caller wants information for positioning on the new link to be
6591 * deposited in *go_line and (if linknum is not NULL) *linknum.
6592 *
6593 * On failure (no more links in the requested direction) returns NO
6594 * and doesn't change *go_line or *linknum. Otherwise, LINK_DO_ARROWUP
6595 * may be returned, and *go_line and *linknum not changed, to indicate that
6596 * the caller should use a normal PREV_LINK or PREV_PAGE mechanism.
6597 * Otherwise:
6598 * The number (0-based counting) for the new top screen line will be returned
6599 * in *go_line, and *linknum will be set to an index into the links[] array,
6600 * to use after the line in *go_line has been made the new top screen
6601 * line. - kw
6602 */
HTGetLinkOrFieldStart(int curlink,int * go_line,int * linknum,int direction,int ta_skip)6603 int HTGetLinkOrFieldStart(int curlink,
6604 int *go_line,
6605 int *linknum,
6606 int direction,
6607 int ta_skip)
6608 {
6609 TextAnchor *a;
6610 int anchors_this_line = 0;
6611 int prev_anchor_line = -1, prev_prev_anchor_line = -1;
6612
6613 struct agroup {
6614 TextAnchor *anc;
6615 int prev_anchor_line;
6616 int anchors_this_line;
6617 int anchors_this_group;
6618 } previous, current;
6619 struct agroup *group_to_go = NULL;
6620
6621 if (!HTMainText)
6622 return (NO);
6623
6624 previous.anc = current.anc = NULL;
6625 previous.prev_anchor_line = current.prev_anchor_line = -1;
6626 previous.anchors_this_line = current.anchors_this_line = 0;
6627 previous.anchors_this_group = current.anchors_this_group = 0;
6628
6629 for (a = HTMainText->first_anchor; a; a = a->next) {
6630 /*
6631 * Count anchors, first on current line if there is more
6632 * than one. We have to count all links, including form
6633 * field anchors and others with a->number == 0, because
6634 * they are or will be included in the links[] array.
6635 * The exceptions are hidden form fields and anchors with
6636 * show_anchor not set, because they won't appear in links[]
6637 * and don't count towards nlinks. - KW
6638 */
6639 if ((a->show_anchor) &&
6640 !(a->link_type == INPUT_ANCHOR
6641 && a->input_field->type == F_HIDDEN_TYPE)) {
6642 if (a->line_num == prev_anchor_line) {
6643 anchors_this_line++;
6644 } else {
6645 /*
6646 * This anchor is on a different line than the previous one.
6647 * Remember which was the line number of the previous anchor,
6648 * for use in screen positioning later. - KW
6649 */
6650 anchors_this_line = 1;
6651 prev_prev_anchor_line = prev_anchor_line;
6652 prev_anchor_line = a->line_num;
6653 }
6654
6655 if (!same_anchors(current.anc, a, ta_skip)) {
6656 previous.anc = current.anc;
6657 previous.prev_anchor_line = current.prev_anchor_line;
6658 previous.anchors_this_line = current.anchors_this_line;
6659 previous.anchors_this_group = current.anchors_this_group;
6660 current.anc = a;
6661 current.prev_anchor_line = prev_prev_anchor_line;
6662 current.anchors_this_line = anchors_this_line;
6663 current.anchors_this_group = 1;
6664 } else {
6665 current.anchors_this_group++;
6666 }
6667 if (curlink >= 0) {
6668 if (same_anchor_as_link(curlink, a, ta_skip)) {
6669 if (direction == -1) {
6670 group_to_go = &previous;
6671 break;
6672 } else if (direction == 0) {
6673 group_to_go = ¤t;
6674 break;
6675 }
6676 } else if (direction > 0 &&
6677 same_anchor_as_link(curlink, previous.anc, ta_skip)) {
6678 group_to_go = ¤t;
6679 break;
6680 }
6681 } else {
6682 if (a->line_num >= HTMainText->top_of_screen) {
6683 if (direction < 0) {
6684 group_to_go = &previous;
6685 break;
6686 } else if (direction == 0) {
6687 if (previous.anc) {
6688 group_to_go = &previous;
6689 break;
6690 } else {
6691 group_to_go = ¤t;
6692 break;
6693 }
6694 } else {
6695 group_to_go = ¤t;
6696 break;
6697 }
6698 }
6699 }
6700 }
6701 }
6702 if (!group_to_go && curlink < 0 && direction <= 0) {
6703 group_to_go = ¤t;
6704 }
6705 if (group_to_go) {
6706 a = group_to_go->anc;
6707 if (a) {
6708 int max_offset;
6709
6710 /*
6711 * We know where to go; most of the stuff below is just
6712 * tweaks to try to position the new screen in a specific
6713 * way.
6714 *
6715 * In some cases going to a previous link can be done
6716 * via the normal LYK_PREV_LINK action, which may give
6717 * better positioning of the new screen. - kw
6718 */
6719 if (a->line_num < HTMainText->top_of_screen &&
6720 a->line_num >= HTMainText->top_of_screen - (display_lines)) {
6721 if ((curlink < 0 &&
6722 group_to_go->anchors_this_group == 1) ||
6723 (direction < 0 &&
6724 group_to_go != ¤t &&
6725 current.anc &&
6726 current.anc->line_num >= HTMainText->top_of_screen &&
6727 group_to_go->anchors_this_group == 1) ||
6728 (a->next &&
6729 a->next->line_num >= HTMainText->top_of_screen)) {
6730 return (LINK_DO_ARROWUP);
6731 }
6732 }
6733 /*
6734 * The fundamental limitation of the current anchors_this_line
6735 * counter method is that we only can set *linknum to the right
6736 * index into the future links[] array if the line with our link
6737 * ends up being the first line with any links (that count) on
6738 * the new screen. Subject to that restriction we still have
6739 * some vertical liberty (sometimes), and try to make the best
6740 * of it. It may be a question of taste though. - kw
6741 */
6742 if (a->line_num <= (display_lines)) {
6743 max_offset = 0;
6744 } else if (a->line_num < HTMainText->top_of_screen) {
6745 int screensback =
6746 (HTMainText->top_of_screen - a->line_num + (display_lines) - 1)
6747 / (display_lines);
6748
6749 max_offset = a->line_num - (HTMainText->top_of_screen -
6750 screensback * (display_lines));
6751 } else if (HTMainText->Lines - a->line_num <= (display_lines)) {
6752 max_offset = a->line_num - (HTMainText->Lines + 1
6753 - (display_lines));
6754 } else if (a->line_num >=
6755 HTMainText->top_of_screen + (display_lines)) {
6756 int screensahead =
6757 (a->line_num - HTMainText->top_of_screen) / (display_lines);
6758
6759 max_offset = a->line_num - HTMainText->top_of_screen -
6760 screensahead * (display_lines);
6761 } else {
6762 max_offset = SEARCH_GOAL_LINE - 1;
6763 }
6764
6765 /* Stuff below should remain unchanged if line positioning
6766 is tweaked. - kw */
6767 if (max_offset < 0)
6768 max_offset = 0;
6769 else if (max_offset >= display_lines)
6770 max_offset = display_lines - 1;
6771 *go_line = a->line_num - max_offset;
6772 if (*go_line <= group_to_go->prev_anchor_line)
6773 *go_line = group_to_go->prev_anchor_line + 1;
6774
6775 if (*go_line < 0)
6776 *go_line = 0;
6777 if (linknum)
6778 *linknum = group_to_go->anchors_this_line - 1;
6779 return (LINK_LINE_FOUND);
6780 }
6781 }
6782 return (NO);
6783 }
6784
6785 /*
6786 * This function finds the line indicated by line_num in the
6787 * HText structure indicated by text, and searches that line
6788 * for the first hit with the string indicated by target. If
6789 * there is no hit, FALSE is returned. If there is a hit, then
6790 * a copy of the line starting at that first hit is loaded into
6791 * *data with all IsSpecial characters stripped, its offset and
6792 * the printable target length (without IsSpecial, or extra CJK
6793 * or utf8 characters) are loaded into *offset and *tLen, and
6794 * TRUE is returned. -FM
6795 */
HText_getFirstTargetInLine(HText * text,int line_num,int utf_flag,int * offset,int * tLen,char ** data,const char * target)6796 BOOL HText_getFirstTargetInLine(HText *text, int line_num,
6797 int utf_flag,
6798 int *offset,
6799 int *tLen,
6800 char **data,
6801 const char *target)
6802 {
6803 HTLine *line;
6804 char *LineData;
6805 int LineOffset, HitOffset, LenNeeded, i;
6806 const char *cp;
6807
6808 /*
6809 * Make sure we have an HText structure, that line_num is
6810 * in its range, and that we have a target string. -FM
6811 */
6812 if (!(text &&
6813 line_num >= 0 &&
6814 line_num <= text->Lines &&
6815 non_empty(target))) {
6816 return (FALSE);
6817 }
6818
6819 /*
6820 * Find the line and set up its data and offset -FM
6821 */
6822 for (i = 0, line = FirstHTLine(text);
6823 i < line_num && (line != text->last_line);
6824 i++, line = line->next) {
6825 if (line->next == NULL) {
6826 return (FALSE);
6827 }
6828 }
6829 if (!(line && line->data[0]))
6830 return (FALSE);
6831 LineData = (char *) line->data;
6832 LineOffset = (int) line->offset;
6833
6834 /*
6835 * If the target is on the line, load the offset of
6836 * its first character and the subsequent line data,
6837 * strip any special characters from the loaded line
6838 * data, and return TRUE. -FM
6839 */
6840 if (((cp = LYno_attr_mb_strstr(LineData,
6841 target,
6842 utf_flag, YES,
6843 &HitOffset,
6844 &LenNeeded)) != NULL) &&
6845 (LineOffset + LenNeeded) <= DISPLAY_COLS) {
6846 /*
6847 * We had a hit so load the results,
6848 * remove IsSpecial characters from
6849 * the allocated data string, and
6850 * return TRUE. -FM
6851 */
6852 *offset = (LineOffset + HitOffset);
6853 *tLen = (LenNeeded - HitOffset);
6854 StrAllocCopy(*data, cp);
6855 remove_special_attr_chars(*data);
6856 return (TRUE);
6857 }
6858
6859 /*
6860 * The line does not contain the target. -FM
6861 */
6862 return (FALSE);
6863 }
6864
6865 /*
6866 * HText_getNumOfLines returns the number of lines in the
6867 * current document.
6868 */
HText_getNumOfLines(void)6869 int HText_getNumOfLines(void)
6870 {
6871 return (HTMainText ? HTMainText->Lines : 0);
6872 }
6873
6874 /*
6875 * HText_getNumOfBytes returns the size of the document, as rendered. This
6876 * may be different from the original filesize.
6877 */
HText_getNumOfBytes(void)6878 int HText_getNumOfBytes(void)
6879 {
6880 int result = -1;
6881 HTLine *line = NULL;
6882
6883 if (HTMainText != 0) {
6884 for (line = FirstHTLine(HTMainText);
6885 line != HTMainText->last_line;
6886 line = line->next) {
6887 result += 1 + (int) strlen(line->data);
6888 }
6889 }
6890 return result;
6891 }
6892
6893 /*
6894 * HText_getTitle returns the title of the
6895 * current document.
6896 */
HText_getTitle(void)6897 const char *HText_getTitle(void)
6898 {
6899 return (HTMainText ?
6900 HTAnchor_title(HTMainText->node_anchor) : 0);
6901 }
6902
6903 #ifdef USE_COLOR_STYLE
HText_getStyle(void)6904 const char *HText_getStyle(void)
6905 {
6906 return (HTMainText ?
6907 HTAnchor_style(HTMainText->node_anchor) : 0);
6908 }
6909 #endif
6910
6911 /*
6912 * HText_getSugFname returns the suggested filename of the current
6913 * document (normally derived from a Content-Disposition header with
6914 * attachment; filename=name.suffix). -FM
6915 */
HText_getSugFname(void)6916 const char *HText_getSugFname(void)
6917 {
6918 return (HTMainText ?
6919 HTAnchor_SugFname(HTMainText->node_anchor) : 0);
6920 }
6921
6922 /*
6923 * HTCheckFnameForCompression receives the address of an allocated
6924 * string containing a filename, and an anchor pointer, and expands
6925 * or truncates the string's suffix if appropriate, based on whether
6926 * the anchor indicates that the file is compressed. We assume
6927 * that the file was not uncompressed (as when downloading), and
6928 * believe the headers about whether it's compressed or not. -FM
6929 *
6930 * Added third arg - if strip_ok is FALSE, we don't trust the anchor
6931 * info enough to remove a compression suffix if the anchor object
6932 * does not indicate compression. - kw
6933 */
HTCheckFnameForCompression(char ** fname,HTParentAnchor * anchor,int strip_ok)6934 void HTCheckFnameForCompression(char **fname,
6935 HTParentAnchor *anchor,
6936 int strip_ok)
6937 {
6938 char *fn = *fname;
6939 char *dot = NULL;
6940 char *cp = NULL;
6941 const char *suffix = "";
6942 CompressFileType method;
6943 CompressFileType second;
6944
6945 /*
6946 * Make sure we have a string and anchor. -FM
6947 */
6948 if (!(fn && anchor))
6949 return;
6950
6951 /*
6952 * Make sure we have a file, not directory, name. -FM
6953 */
6954 if (*(fn = LYPathLeaf(fn)) == '\0')
6955 return;
6956
6957 method = HTContentToCompressType(anchor);
6958
6959 /*
6960 * If no Content-Encoding has been detected via the anchor
6961 * pointer, but strip_ok is not set, there is nothing left
6962 * to do. - kw
6963 */
6964 if ((method == cftNone) && !strip_ok)
6965 return;
6966
6967 /*
6968 * Treat .tgz specially
6969 */
6970 if ((dot = strrchr(fn, '.')) != NULL
6971 && !strcasecomp(dot, ".tgz")) {
6972 if (method == cftNone) {
6973 strcpy(dot, ".tar");
6974 }
6975 return;
6976 }
6977
6978 /*
6979 * Seek the last dot, and check whether
6980 * we have a gzip or compress suffix. -FM
6981 */
6982 if ((dot = strrchr(fn, '.')) != NULL) {
6983 int rootlen = 0;
6984
6985 if (HTCompressFileType(fn, ".", &rootlen) != cftNone) {
6986 if (method == cftNone) {
6987 /*
6988 * It has a suffix which signifies a gzipped
6989 * or compressed file for us, but the anchor
6990 * claims otherwise, so tweak the suffix. -FM
6991 */
6992 *dot = '\0';
6993 }
6994 return;
6995 }
6996 if ((second = HTCompressFileType(fn, "-_", &rootlen)) != cftNone) {
6997 cp = fn + rootlen;
6998 if (method == cftNone) {
6999 /*
7000 * It has a tail which signifies a gzipped
7001 * file for us, but the anchor claims otherwise,
7002 * so tweak the suffix. -FM
7003 */
7004 if (cp == dot + 1)
7005 cp--;
7006 *cp = '\0';
7007 } else {
7008 /*
7009 * The anchor claims it's gzipped, and we
7010 * believe it, so force this tail to the
7011 * conventional suffix. -FM
7012 */
7013 #ifdef VMS
7014 *cp = '-';
7015 #else
7016 *cp = '.';
7017 #endif /* VMS */
7018 if (second == cftCompress)
7019 LYUpperCase(cp);
7020 else
7021 LYLowerCase(cp);
7022 }
7023 return;
7024 }
7025 }
7026
7027 suffix = HTCompressTypeToSuffix(method);
7028
7029 /*
7030 * Add the appropriate suffix. -FM
7031 */
7032 if (*suffix) {
7033 if (!dot) {
7034 StrAllocCat(*fname, suffix);
7035 } else if (*++dot == '\0') {
7036 StrAllocCat(*fname, suffix + 1);
7037 } else {
7038 StrAllocCat(*fname, suffix);
7039 #ifdef VMS
7040 (*fname)[strlen(*fname) - strlen(suffix)] = '-';
7041 #endif /* !VMS */
7042 }
7043 }
7044 }
7045
7046 /*
7047 * HText_getLastModified returns the Last-Modified header
7048 * if available, for the current document. -FM
7049 */
HText_getLastModified(void)7050 const char *HText_getLastModified(void)
7051 {
7052 return (HTMainText ?
7053 HTAnchor_last_modified(HTMainText->node_anchor) : 0);
7054 }
7055
7056 /*
7057 * HText_getDate returns the Date header
7058 * if available, for the current document. -FM
7059 */
HText_getDate(void)7060 const char *HText_getDate(void)
7061 {
7062 return (HTMainText ?
7063 HTAnchor_date(HTMainText->node_anchor) : 0);
7064 }
7065
7066 /*
7067 * HText_getServer returns the Server header
7068 * if available, for the current document. -FM
7069 */
HText_getServer(void)7070 const char *HText_getServer(void)
7071 {
7072 return (HTMainText ?
7073 HTAnchor_server(HTMainText->node_anchor) : 0);
7074 }
7075
7076 /*
7077 * Returns the full text of HTTP headers, if available, for the current
7078 * document.
7079 */
HText_getHttpHeaders(void)7080 const char *HText_getHttpHeaders(void)
7081 {
7082 return (HTMainText ?
7083 HTAnchor_http_headers(HTMainText->node_anchor) : 0);
7084 }
7085
7086 /*
7087 * HText_pageDisplay displays a screen of text
7088 * starting from the line 'line_num'-1.
7089 * This is the primary call for lynx.
7090 */
HText_pageDisplay(int line_num,char * target)7091 void HText_pageDisplay(int line_num,
7092 char *target)
7093 {
7094 #ifdef DISP_PARTIAL
7095 if (debug_display_partial || (LYTraceLogFP != NULL)) {
7096 CTRACE((tfp, "GridText: HText_pageDisplay at line %d started\n", line_num));
7097 }
7098
7099 if (display_partial) {
7100 int stop_before = -1;
7101
7102 /*
7103 * Garbage is reported from forms input fields in incremental mode.
7104 * So we start HText_trimHightext() to forget this side effect.
7105 * This function was split-out from HText_endAppend().
7106 * It may not be the best solution but it works. - LP
7107 *
7108 * (FALSE = indicate that we are in partial mode)
7109 * Multiple calls of HText_trimHightext works without problem now.
7110 */
7111 if (HTMainText && HTMainText->stbl)
7112 stop_before = Stbl_getStartLineDeep(HTMainText->stbl);
7113 HText_trimHightext(HTMainText, FALSE, stop_before);
7114 }
7115 #endif
7116 display_page(HTMainText, line_num - 1, target);
7117
7118 #ifdef DISP_PARTIAL
7119 if (display_partial && debug_display_partial)
7120 LYSleepMsg();
7121 #endif
7122
7123 is_www_index = HTAnchor_isIndex(HTMainAnchor);
7124
7125 #ifdef DISP_PARTIAL
7126 if (debug_display_partial || (LYTraceLogFP != NULL)) {
7127 CTRACE((tfp, "GridText: HText_pageDisplay finished\n"));
7128 }
7129 #endif
7130 }
7131
7132 /*
7133 * Return YES if we have a whereis search target on the displayed
7134 * page. - kw
7135 */
HText_pageHasPrevTarget(void)7136 BOOL HText_pageHasPrevTarget(void)
7137 {
7138 if (!HTMainText)
7139 return NO;
7140 else
7141 return HTMainText->page_has_target;
7142 }
7143
7144 /*
7145 * Find the number of the closest anchor to the given document offset. Used
7146 * in reparsing, this will usually find an exact match, as a link shifts around
7147 * on the display. It will not find a match when (for example) the source view
7148 * shows images that are not links in the html.
7149 */
HText_closestAnchor(HText * text,int offset)7150 int HText_closestAnchor(HText *text, int offset)
7151 {
7152 int result = -1;
7153 int absdiff = 0;
7154 int newdiff;
7155 TextAnchor *Anchor_ptr = NULL;
7156 TextAnchor *closest = NULL;
7157
7158 for (Anchor_ptr = text->first_anchor;
7159 Anchor_ptr != NULL;
7160 Anchor_ptr = Anchor_ptr->next) {
7161 if (Anchor_ptr->sgml_offset == offset) {
7162 result = Anchor_ptr->number;
7163 break;
7164 } else {
7165 newdiff = abs(Anchor_ptr->sgml_offset - offset);
7166 if (absdiff == 0 || absdiff > newdiff) {
7167 absdiff = newdiff;
7168 closest = Anchor_ptr;
7169 }
7170 }
7171 }
7172 if (result < 0 && closest != 0) {
7173 result = closest->number;
7174 }
7175
7176 return result;
7177 }
7178
7179 /*
7180 * Find the offset for the given anchor, e.g., the inverse of
7181 * HText_closestAnchor().
7182 */
HText_locateAnchor(HText * text,int anchor_number)7183 int HText_locateAnchor(HText *text, int anchor_number)
7184 {
7185 int result = -1;
7186 TextAnchor *Anchor_ptr = NULL;
7187
7188 for (Anchor_ptr = text->first_anchor;
7189 Anchor_ptr != NULL;
7190 Anchor_ptr = Anchor_ptr->next) {
7191 if (Anchor_ptr->number == anchor_number) {
7192 result = Anchor_ptr->sgml_offset;
7193 break;
7194 }
7195 }
7196
7197 return result;
7198 }
7199
7200 /*
7201 * This is supposed to give the same result as the inline checks in
7202 * display_page(), so we can decide which anchors will be visible.
7203 */
anchor_is_numbered(TextAnchor * Anchor_ptr)7204 static BOOL anchor_is_numbered(TextAnchor *Anchor_ptr)
7205 {
7206 BOOL result = FALSE;
7207
7208 if (Anchor_ptr->show_anchor
7209 && (Anchor_ptr->link_type & HYPERTEXT_ANCHOR)) {
7210 result = TRUE;
7211 } else if (Anchor_ptr->link_type == INPUT_ANCHOR
7212 && Anchor_ptr->input_field->type != F_HIDDEN_TYPE) {
7213 result = TRUE;
7214 }
7215 return result;
7216 }
7217
7218 /*
7219 * Return the absolute line number (counting from the beginning of the
7220 * document) for the given absolute anchor number. Normally line numbers are
7221 * computed within the screen, and for that we use the links[] array. A few
7222 * uses require the absolute anchor number. For example, reparsing a document,
7223 * e.g., switching between normal and source views will alter the line numbers
7224 * of each link, and may require adjusting the top line number used for the
7225 * display, before we recompute the links[] array.
7226 */
HText_getAbsLineNumber(HText * text,int anchor_number)7227 int HText_getAbsLineNumber(HText *text,
7228 int anchor_number)
7229 {
7230 int result = -1;
7231
7232 if (anchor_number >= 0 && text != 0) {
7233 TextAnchor *Anchor_ptr = NULL;
7234
7235 for (Anchor_ptr = text->first_anchor;
7236 Anchor_ptr != NULL;
7237 Anchor_ptr = Anchor_ptr->next) {
7238 if (anchor_is_numbered(Anchor_ptr)
7239 && Anchor_ptr->number == anchor_number) {
7240 result = Anchor_ptr->line_num;
7241 break;
7242 }
7243 }
7244 }
7245 return result;
7246 }
7247
7248 /*
7249 * Compute the link-number in a page, given the top line number of the page and
7250 * the absolute anchor number.
7251 */
HText_anchorRelativeTo(HText * text,int top_lineno,int anchor_number)7252 int HText_anchorRelativeTo(HText *text, int top_lineno, int anchor_number)
7253 {
7254 int result = 0;
7255 int from_top = 0;
7256 TextAnchor *Anchor_ptr = NULL;
7257
7258 for (Anchor_ptr = text->first_anchor;
7259 Anchor_ptr != NULL;
7260 Anchor_ptr = Anchor_ptr->next) {
7261 if (Anchor_ptr->number == anchor_number) {
7262 result = from_top;
7263 break;
7264 }
7265 if (!anchor_is_numbered(Anchor_ptr))
7266 continue;
7267 if (Anchor_ptr->line_num >= top_lineno) {
7268 ++from_top;
7269 }
7270 }
7271 return result;
7272 }
7273
7274 /*
7275 * HText_LinksInLines returns the number of links in the
7276 * 'Lines' number of lines beginning with 'line_num'-1. -FM
7277 */
HText_LinksInLines(HText * text,int line_num,int Lines)7278 int HText_LinksInLines(HText *text,
7279 int line_num,
7280 int Lines)
7281 {
7282 int total = 0;
7283 int start = (line_num - 1);
7284 int end = (start + Lines);
7285 TextAnchor *Anchor_ptr = NULL;
7286
7287 if (!text)
7288 return total;
7289
7290 for (Anchor_ptr = text->first_anchor;
7291 Anchor_ptr != NULL && Anchor_ptr->line_num <= end;
7292 Anchor_ptr = Anchor_ptr->next) {
7293 if (Anchor_ptr->line_num >= start &&
7294 Anchor_ptr->line_num < end &&
7295 Anchor_ptr->show_anchor &&
7296 !(Anchor_ptr->link_type == INPUT_ANCHOR
7297 && Anchor_ptr->input_field->type == F_HIDDEN_TYPE))
7298 ++total;
7299 }
7300
7301 return total;
7302 }
7303
HText_setStale(HText * text)7304 void HText_setStale(HText *text)
7305 {
7306 text->stale = YES;
7307 }
7308
HText_refresh(HText * text)7309 void HText_refresh(HText *text)
7310 {
7311 if (text->stale)
7312 display_page(text, text->top_of_screen, "");
7313 }
7314
HText_sourceAnchors(HText * text)7315 int HText_sourceAnchors(HText *text)
7316 {
7317 return (text ? text->last_anchor_number : -1);
7318 }
7319
HText_canScrollUp(HText * text)7320 BOOL HText_canScrollUp(HText *text)
7321 {
7322 return (BOOL) (text->top_of_screen != 0);
7323 }
7324
7325 /*
7326 * Check if there is more info below this page.
7327 */
HText_canScrollDown(void)7328 BOOL HText_canScrollDown(void)
7329 {
7330 HText *text = HTMainText;
7331
7332 return (BOOL) ((text != 0)
7333 && ((text->top_of_screen + display_lines) <= text->Lines));
7334 }
7335
7336 /* Scroll actions
7337 */
HText_scrollTop(HText * text)7338 void HText_scrollTop(HText *text)
7339 {
7340 display_page(text, 0, "");
7341 }
7342
HText_scrollDown(HText * text)7343 void HText_scrollDown(HText *text)
7344 {
7345 display_page(text, text->top_of_screen + display_lines, "");
7346 }
7347
HText_scrollUp(HText * text)7348 void HText_scrollUp(HText *text)
7349 {
7350 display_page(text, text->top_of_screen - display_lines, "");
7351 }
7352
HText_scrollBottom(HText * text)7353 void HText_scrollBottom(HText *text)
7354 {
7355 display_page(text, text->Lines - display_lines, "");
7356 }
7357
7358 /* Browsing functions
7359 * ==================
7360 */
7361
7362 /* Bring to front and highlight it
7363 */
HText_select(HText * text)7364 BOOL HText_select(HText *text)
7365 {
7366 if (text != HTMainText) {
7367 /*
7368 * Reset flag for whereis search string - cannot be true here
7369 * since text is not our HTMainText. - kw
7370 */
7371 if (text)
7372 text->page_has_target = NO;
7373
7374 #ifdef DISP_PARTIAL
7375 /* Reset these for the previous and current text. - kw */
7376 ResetPartialLinenos(text);
7377 ResetPartialLinenos(HTMainText);
7378 #endif /* DISP_PARTIAL */
7379
7380 #ifdef CAN_SWITCH_DISPLAY_CHARSET
7381 /* text->UCLYhndl is not reset by META, so use a more circumvent way */
7382 if (text->node_anchor->UCStages->s[UCT_STAGE_HTEXT].LYhndl
7383 != current_char_set)
7384 Switch_Display_Charset(text->node_anchor->UCStages->s[UCT_STAGE_HTEXT].LYhndl, SWITCH_DISPLAY_CHARSET_MAYBE);
7385 #endif
7386 assert(text != NULL);
7387 if (HTMainText) {
7388 if (HText_hasUTF8OutputSet(HTMainText) &&
7389 HTLoadedDocumentEightbit() &&
7390 IS_UTF8_TTY) {
7391 text->had_utf8 = HTMainText->has_utf8;
7392 } else {
7393 text->had_utf8 = NO;
7394 }
7395 HTMainText->has_utf8 = NO;
7396 text->has_utf8 = NO;
7397 }
7398
7399 HTMainText = text;
7400 HTMainAnchor = text->node_anchor;
7401
7402 /*
7403 * Make this text the most current in the loaded texts list. -FM
7404 */
7405 if (loaded_texts && HTList_removeObject(loaded_texts, text))
7406 HTList_addObject(loaded_texts, text);
7407 }
7408 return YES;
7409 }
7410
7411 /*
7412 * This function returns TRUE if doc's post_data, address
7413 * and isHEAD elements are identical to those of a loaded
7414 * (memory cached) text. -FM
7415 */
HText_POSTReplyLoaded(DocInfo * doc)7416 BOOL HText_POSTReplyLoaded(DocInfo *doc)
7417 {
7418 HText *text = NULL;
7419 HTList *cur = loaded_texts;
7420 bstring *post_data;
7421 char *address;
7422 BOOL is_head;
7423
7424 /*
7425 * Make sure we have the structures. -FM
7426 */
7427 if (!cur || !doc)
7428 return (FALSE);
7429
7430 /*
7431 * Make sure doc is for a POST reply. -FM
7432 */
7433 if ((post_data = doc->post_data) == NULL ||
7434 (address = doc->address) == NULL)
7435 return (FALSE);
7436 is_head = doc->isHEAD;
7437
7438 /*
7439 * Loop through the loaded texts looking for a
7440 * POST reply match. -FM
7441 */
7442 while (NULL != (text = (HText *) HTList_nextObject(cur))) {
7443 if (text->node_anchor &&
7444 text->node_anchor->post_data &&
7445 BINEQ(post_data, text->node_anchor->post_data) &&
7446 text->node_anchor->address &&
7447 !strcmp(address, text->node_anchor->address) &&
7448 is_head == text->node_anchor->isHEAD) {
7449 return (TRUE);
7450 }
7451 }
7452
7453 return (FALSE);
7454 }
7455
HTFindPoundSelector(const char * selector)7456 BOOL HTFindPoundSelector(const char *selector)
7457 {
7458 TextAnchor *a;
7459
7460 CTRACE((tfp, "FindPound: searching for \"%s\"\n", selector));
7461 for (a = HTMainText->first_anchor; a != 0; a = a->next) {
7462
7463 if (a->anchor && a->anchor->tag) {
7464 if (!strcmp(a->anchor->tag, selector)) {
7465
7466 www_search_result = a->line_num + 1;
7467
7468 CTRACE((tfp, "FindPound: Selecting anchor [%d] at line %d\n",
7469 a->number, www_search_result));
7470 if (!strcmp(selector, LYToolbarName)) {
7471 --www_search_result;
7472 }
7473 return (YES);
7474 }
7475 }
7476 }
7477 return (NO);
7478 }
7479
HText_selectAnchor(HText * text,HTChildAnchor * anchor)7480 BOOL HText_selectAnchor(HText *text, HTChildAnchor *anchor)
7481 {
7482 TextAnchor *a;
7483 int l;
7484
7485 for (a = text->first_anchor; a; a = a->next) {
7486 if (a->anchor == anchor)
7487 break;
7488 }
7489 if (!a) {
7490 CTRACE((tfp, "HText: No such anchor in this text!\n"));
7491 return NO;
7492 }
7493
7494 if (text != HTMainText) { /* Comment out by ??? */
7495 HTMainText = text; /* Put back in by tbl 921208 */
7496 HTMainAnchor = text->node_anchor;
7497 }
7498 l = a->line_num;
7499
7500 CTRACE((tfp, "HText: Selecting anchor [%d] at line %d\n",
7501 a->number, l));
7502
7503 if (!text->stale &&
7504 (l >= text->top_of_screen) &&
7505 (l < text->top_of_screen + display_lines + 1))
7506 return YES;
7507
7508 www_search_result = l - (display_lines / 3); /* put in global variable */
7509
7510 return YES;
7511 }
7512
7513 /* Editing functions - NOT IMPLEMENTED
7514 * =================
7515 *
7516 * These are called from the application. There are many more functions
7517 * not included here from the original text object.
7518 */
7519
7520 /* Style handling:
7521 */
7522 /* Apply this style to the selection
7523 */
HText_applyStyle(HText * me GCC_UNUSED,HTStyle * style GCC_UNUSED)7524 void HText_applyStyle(HText *me GCC_UNUSED, HTStyle *style GCC_UNUSED)
7525 {
7526
7527 }
7528
7529 /* Update all text with changed style.
7530 */
HText_updateStyle(HText * me GCC_UNUSED,HTStyle * style GCC_UNUSED)7531 void HText_updateStyle(HText *me GCC_UNUSED, HTStyle *style GCC_UNUSED)
7532 {
7533
7534 }
7535
7536 /* Return style of selection
7537 */
HText_selectionStyle(HText * me GCC_UNUSED,HTStyleSheet * sheet GCC_UNUSED)7538 HTStyle *HText_selectionStyle(HText *me GCC_UNUSED, HTStyleSheet *sheet GCC_UNUSED)
7539 {
7540 return 0;
7541 }
7542
7543 /* Paste in styled text
7544 */
HText_replaceSel(HText * me GCC_UNUSED,const char * aString GCC_UNUSED,HTStyle * aStyle GCC_UNUSED)7545 void HText_replaceSel(HText *me GCC_UNUSED, const char *aString GCC_UNUSED,
7546 HTStyle *aStyle GCC_UNUSED)
7547 {
7548 }
7549
7550 /* Apply this style to the selection and all similarly formatted text
7551 * (style recovery only)
7552 */
HTextApplyToSimilar(HText * me GCC_UNUSED,HTStyle * style GCC_UNUSED)7553 void HTextApplyToSimilar(HText *me GCC_UNUSED, HTStyle *style GCC_UNUSED)
7554 {
7555
7556 }
7557
7558 /* Select the first unstyled run.
7559 * (style recovery only)
7560 */
HTextSelectUnstyled(HText * me GCC_UNUSED,HTStyleSheet * sheet GCC_UNUSED)7561 void HTextSelectUnstyled(HText *me GCC_UNUSED, HTStyleSheet *sheet GCC_UNUSED)
7562 {
7563
7564 }
7565
7566 /* Anchor handling:
7567 */
HText_unlinkSelection(HText * me GCC_UNUSED)7568 void HText_unlinkSelection(HText *me GCC_UNUSED)
7569 {
7570
7571 }
7572
HText_referenceSelected(HText * me GCC_UNUSED)7573 HTAnchor *HText_referenceSelected(HText *me GCC_UNUSED)
7574 {
7575 return 0;
7576 }
7577
HText_getTopOfScreen(void)7578 int HText_getTopOfScreen(void)
7579 {
7580 HText *text = HTMainText;
7581
7582 return text != 0 ? text->top_of_screen : 0;
7583 }
7584
HText_getLines(HText * text)7585 int HText_getLines(HText *text)
7586 {
7587 return text->Lines;
7588 }
7589
7590 /*
7591 * Constrain the line number to be within the document. The line number is
7592 * zero-based.
7593 */
HText_getPreferredTopLine(HText * text,int line_number)7594 int HText_getPreferredTopLine(HText *text, int line_number)
7595 {
7596 int last_screen = text->Lines - (display_lines - 2);
7597
7598 if (text->Lines < display_lines) {
7599 line_number = 0;
7600 } else if (line_number > text->Lines) {
7601 line_number = last_screen;
7602 } else if (line_number < 0) {
7603 line_number = 0;
7604 }
7605 return line_number;
7606 }
7607
HText_linkSelTo(HText * me GCC_UNUSED,HTAnchor * anchor GCC_UNUSED)7608 HTAnchor *HText_linkSelTo(HText *me GCC_UNUSED,
7609 HTAnchor * anchor GCC_UNUSED)
7610 {
7611 return 0;
7612 }
7613
7614 /*
7615 * Utility for freeing the list of previous isindex and whereis queries. -FM
7616 */
HTSearchQueries_free(void)7617 void HTSearchQueries_free(void)
7618 {
7619 LYFreeStringList(search_queries);
7620 search_queries = NULL;
7621 }
7622
7623 /*
7624 * Utility for listing isindex and whereis queries, making
7625 * any repeated queries the most current in the list. -FM
7626 */
HTAddSearchQuery(char * query)7627 void HTAddSearchQuery(char *query)
7628 {
7629 char *new_query = NULL;
7630 char *old;
7631 HTList *cur;
7632
7633 if (!non_empty(query))
7634 return;
7635
7636 StrAllocCopy(new_query, query);
7637
7638 if (!search_queries) {
7639 search_queries = HTList_new();
7640 #ifdef LY_FIND_LEAKS
7641 atexit(HTSearchQueries_free);
7642 #endif
7643 HTList_addObject(search_queries, new_query);
7644 return;
7645 }
7646
7647 cur = search_queries;
7648 while (NULL != (old = (char *) HTList_nextObject(cur))) {
7649 if (!strcmp(old, new_query)) {
7650 HTList_removeObject(search_queries, old);
7651 FREE(old);
7652 break;
7653 }
7654 }
7655 HTList_addObject(search_queries, new_query);
7656
7657 return;
7658 }
7659
do_www_search(DocInfo * doc)7660 int do_www_search(DocInfo *doc)
7661 {
7662 bstring *searchstring = NULL;
7663 bstring *temp = NULL;
7664 char *cp;
7665 char *tmpaddress = NULL;
7666 int ch;
7667 RecallType recall;
7668 int QueryTotal;
7669 int QueryNum;
7670 BOOLEAN PreviousSearch = FALSE;
7671 int code;
7672
7673 /*
7674 * Load the default query buffer
7675 */
7676 if ((cp = StrChr(doc->address, '?')) != NULL) {
7677 /*
7678 * This is an index from a previous search.
7679 * Use its query as the default.
7680 */
7681 PreviousSearch = TRUE;
7682 BStrCopy0(searchstring, ++cp);
7683 for (cp = searchstring->str; *cp; cp++)
7684 if (*cp == '+')
7685 *cp = ' ';
7686 HTUnEscape(searchstring->str);
7687 BStrCopy(temp, searchstring);
7688 /*
7689 * Make sure it's treated as the most recent query. -FM
7690 */
7691 HTAddSearchQuery(searchstring->str);
7692 } else {
7693 /*
7694 * New search; no default.
7695 */
7696 BStrCopy0(searchstring, "");
7697 BStrCopy0(temp, "");
7698 }
7699
7700 /*
7701 * Prompt for a query string.
7702 */
7703 if (isBEmpty(searchstring)) {
7704 if (HTMainAnchor->isIndexPrompt)
7705 _statusline(HTMainAnchor->isIndexPrompt);
7706 else
7707 _statusline(ENTER_DATABASE_QUERY);
7708 } else
7709 _statusline(EDIT_CURRENT_QUERY);
7710 QueryTotal = (search_queries ? HTList_count(search_queries) : 0);
7711 recall = (((PreviousSearch && QueryTotal >= 2) ||
7712 (!PreviousSearch && QueryTotal >= 1)) ? RECALL_URL : NORECALL);
7713 QueryNum = QueryTotal;
7714
7715 get_query:
7716 if ((ch = LYgetBString(&searchstring, FALSE, 0, recall)) < 0 ||
7717 isBEmpty(searchstring) ||
7718 ch == UPARROW_KEY ||
7719 ch == DNARROW_KEY) {
7720
7721 if (recall && ch == UPARROW_KEY) {
7722 if (PreviousSearch) {
7723 /*
7724 * Use the second to last query in the list. -FM
7725 */
7726 QueryNum = 1;
7727 PreviousSearch = FALSE;
7728 } else {
7729 /*
7730 * Go back to the previous query in the list. -FM
7731 */
7732 QueryNum++;
7733 }
7734 if (QueryNum >= QueryTotal)
7735 /*
7736 * Roll around to the last query in the list. -FM
7737 */
7738 QueryNum = 0;
7739 if ((cp = (char *) HTList_objectAt(search_queries,
7740 QueryNum)) != NULL) {
7741 BStrCopy0(searchstring, cp);
7742 if (!isBEmpty(temp) &&
7743 !strcmp(temp->str, searchstring->str)) {
7744 _statusline(EDIT_CURRENT_QUERY);
7745 } else if ((!isBEmpty(temp) && QueryTotal == 2) ||
7746 (isBEmpty(temp) && QueryTotal == 1)) {
7747 _statusline(EDIT_THE_PREV_QUERY);
7748 } else {
7749 _statusline(EDIT_A_PREV_QUERY);
7750 }
7751 goto get_query;
7752 }
7753 } else if (recall && ch == DNARROW_KEY) {
7754 if (PreviousSearch) {
7755 /*
7756 * Use the first query in the list. -FM
7757 */
7758 QueryNum = QueryTotal - 1;
7759 PreviousSearch = FALSE;
7760 } else {
7761 /*
7762 * Advance to the next query in the list. -FM
7763 */
7764 QueryNum--;
7765 }
7766 if (QueryNum < 0)
7767 /*
7768 * Roll around to the first query in the list. -FM
7769 */
7770 QueryNum = QueryTotal - 1;
7771 if ((cp = (char *) HTList_objectAt(search_queries,
7772 QueryNum)) != NULL) {
7773 BStrCopy0(searchstring, cp);
7774 if (!isBEmpty(temp) &&
7775 !strcmp(temp->str, searchstring->str)) {
7776 _statusline(EDIT_CURRENT_QUERY);
7777 } else if ((!isBEmpty(temp) && QueryTotal == 2) ||
7778 (isBEmpty(temp) && QueryTotal == 1)) {
7779 _statusline(EDIT_THE_PREV_QUERY);
7780 } else {
7781 _statusline(EDIT_A_PREV_QUERY);
7782 }
7783 goto get_query;
7784 }
7785 }
7786
7787 /*
7788 * Search cancelled.
7789 */
7790 HTInfoMsg(CANCELLED);
7791 code = NULLFILE;
7792 } else {
7793
7794 LYTrimLeading(searchstring->str);
7795 LYTrimTrailing(searchstring->str);
7796 if (isBEmpty(searchstring)) {
7797 HTInfoMsg(CANCELLED);
7798 code = NULLFILE;
7799 } else if (!LYforce_no_cache &&
7800 !isBEmpty(temp) &&
7801 !strcmp(temp->str, searchstring->str)) {
7802 /*
7803 * Don't resubmit the same query unintentionally.
7804 */
7805 HTUserMsg(USE_C_R_TO_RESUB_CUR_QUERY);
7806 code = NULLFILE;
7807 } else {
7808
7809 /*
7810 * Add searchstring to the query list,
7811 * or make it the most current. -FM
7812 */
7813 HTAddSearchQuery(searchstring->str);
7814
7815 /*
7816 * Show the URL with the new query.
7817 */
7818 if ((cp = StrChr(doc->address, '?')) != NULL)
7819 *cp = '\0';
7820 StrAllocCopy(tmpaddress, doc->address);
7821 StrAllocCat(tmpaddress, "?");
7822 StrAllocCat(tmpaddress, searchstring->str);
7823 user_message(WWW_WAIT_MESSAGE, tmpaddress);
7824 #ifdef SYSLOG_REQUESTED_URLS
7825 LYSyslog(tmpaddress);
7826 #endif
7827 FREE(tmpaddress);
7828 if (cp)
7829 *cp = '?';
7830
7831 /*
7832 * OK, now we do the search.
7833 */
7834 if (HTSearch(searchstring->str, HTMainAnchor)) {
7835 auto char *cp_freeme = NULL;
7836
7837 if (traversal)
7838 cp_freeme = stub_HTAnchor_address((HTAnchor *) HTMainAnchor);
7839 else
7840 cp_freeme = HTAnchor_address((HTAnchor *) HTMainAnchor);
7841 StrAllocCopy(doc->address, cp_freeme);
7842 FREE(cp_freeme);
7843
7844 CTRACE((tfp, "\ndo_www_search: newfile: %s\n", doc->address));
7845
7846 /*
7847 * Yah, the search succeeded.
7848 */
7849 code = NORMAL;
7850 } else {
7851
7852 /*
7853 * Either the search failed (Yuk), or we got redirection.
7854 * If it's redirection, use_this_url_instead is set, and
7855 * mainloop() will deal with it such that security features
7856 * and restrictions are checked before acting on the URL, or
7857 * rejecting it. -FM
7858 */
7859 code = NOT_FOUND;
7860 }
7861 }
7862 }
7863 BStrFree(searchstring);
7864 BStrFree(temp);
7865 return code;
7866 }
7867
write_offset(FILE * fp,HTLine * line)7868 static void write_offset(FILE *fp, HTLine *line)
7869 {
7870 int i;
7871
7872 if (line->data[0]) {
7873 for (i = 0; i < (int) line->offset; i++) {
7874 fputc(' ', fp);
7875 }
7876 }
7877 }
7878
write_hyphen(FILE * fp)7879 static void write_hyphen(FILE *fp)
7880 {
7881 if (dump_output_immediately &&
7882 LYRawMode &&
7883 LYlowest_eightbit[current_char_set] <= 173 &&
7884 (LYCharSet_UC[current_char_set].enc == UCT_ENC_8859 ||
7885 (LYCharSet_UC[current_char_set].like8859 & UCT_R_8859SPECL)) != 0) {
7886 fputc(0xad, fp); /* the iso8859 byte for SHY */
7887 } else {
7888 fputc('-', fp);
7889 }
7890 }
7891
7892 /*
7893 * Returns the length after trimming trailing blanks. Modify the string as
7894 * needed so that any special character which follows a trailing blank is moved
7895 * before the (trimmed) blank, so the result which will be dumped has no
7896 * trailing blanks.
7897 */
TrimmedLength(char * string)7898 static int TrimmedLength(char *string)
7899 {
7900 int result = (int) strlen(string);
7901
7902 if (!HTisDocumentSource()) {
7903 int adjust = result;
7904 int ch;
7905
7906 while (adjust > 0) {
7907 ch = UCH(string[adjust - 1]);
7908 if (isspace(ch) || IsSpecialAttrChar(ch)) {
7909 --adjust;
7910 } else {
7911 break;
7912 }
7913 }
7914 if (result != adjust) {
7915 char *dst = string + adjust;
7916 char *src = dst;
7917
7918 for (;;) {
7919 src = LYSkipBlanks(src);
7920 if ((*dst++ = *src++) == '\0')
7921 break;
7922 }
7923 result = (int) (dst - string - 1);
7924 }
7925 }
7926 return result;
7927 }
7928
7929 typedef struct _AnchorIndex {
7930 struct _AnchorIndex *next;
7931 int type; /* field type */
7932 int size; /* character-width of field */
7933 int length; /* byte-count for field's data */
7934 int offset; /* byte-offset in line's data */
7935 char filler; /* character to use for filler */
7936 const char *value; /* field's value */
7937 } AnchorIndex;
7938
countHTLines(void)7939 static unsigned countHTLines(void)
7940 {
7941 unsigned result = 0;
7942 HTLine *line = FirstHTLine(HTMainText);
7943
7944 while (line != 0) {
7945 ++result;
7946 if (line == HTMainText->last_line)
7947 break;
7948 line = line->next;
7949 }
7950 CTRACE((tfp, "countHTLines %u\n", result));
7951 return result;
7952 }
7953
7954 /*
7955 * The TextAnchor list is not organized to allow efficient dumping of a page.
7956 * Make an array with one item per line of the page, and store (by byte-offset)
7957 * pointers to the TextAnchor's we want to use.
7958 */
allocAnchorIndex(unsigned * size)7959 static AnchorIndex **allocAnchorIndex(unsigned *size)
7960 {
7961 AnchorIndex **result = NULL;
7962 AnchorIndex *p, *q;
7963 TextAnchor *anchor = NULL;
7964 FormInfo *input = NULL;
7965
7966 *size = countHTLines();
7967 if (*size != 0) {
7968 result = typecallocn(AnchorIndex *, *size + 1);
7969 if (result == NULL)
7970 outofmem(__FILE__, "allocAnchorIndex");
7971
7972 for (anchor = HTMainText->first_anchor;
7973 anchor != NULL;
7974 anchor = anchor->next) {
7975
7976 if (anchor->link_type == INPUT_ANCHOR
7977 && anchor->show_anchor
7978 && anchor->line_num < (int) *size
7979 && (input = anchor->input_field) != NULL) {
7980 CTRACE2(TRACE_GRIDTEXT,
7981 (tfp, "line %d.%d %d %s->%s(%s)\n",
7982 anchor->line_num,
7983 anchor->line_pos,
7984 input->size,
7985 inputFieldDesc(input),
7986 input->value,
7987 input->orig_value));
7988 switch (input->type) {
7989 case F_SUBMIT_TYPE:
7990 case F_RESET_TYPE:
7991 case F_TEXT_SUBMIT_TYPE:
7992 case F_IMAGE_SUBMIT_TYPE:
7993 CTRACE2(TRACE_GRIDTEXT, (tfp, "skipping\n"));
7994 continue;
7995 case F_TEXT_TYPE:
7996 case F_PASSWORD_TYPE:
7997 case F_CHECKBOX_TYPE:
7998 case F_RADIO_TYPE:
7999 case F_OPTION_LIST_TYPE:
8000 case F_TEXTAREA_TYPE:
8001 case F_RANGE_TYPE:
8002 case F_FILE_TYPE:
8003 p = typecalloc(AnchorIndex);
8004 if (p == NULL)
8005 outofmem(__FILE__, "allocAnchorIndex");
8006
8007 p->type = input->type;
8008 p->size = input->size;
8009 p->offset = anchor->line_pos;
8010 p->value = input->value;
8011
8012 switch (input->type) {
8013 case F_TEXTAREA_TYPE:
8014 case F_TEXT_TYPE:
8015 case F_PASSWORD_TYPE:
8016 p->filler = '_';
8017 break;
8018 case F_OPTION_LIST_TYPE:
8019 p->filler = '_';
8020 break;
8021 case F_CHECKBOX_TYPE:
8022 p->value = (input->num_value
8023 ? checked_box
8024 : unchecked_box);
8025 break;
8026 case F_RADIO_TYPE:
8027 p->value = (input->num_value
8028 ? checked_radio
8029 : unchecked_radio);
8030 break;
8031 default:
8032 p->filler = ' ';
8033 break;
8034 }
8035 p->length = (int) strlen(p->value);
8036
8037 if ((q = result[anchor->line_num]) != NULL) {
8038 /* insert, ordering by offset */
8039 if (q->offset < p->offset) {
8040 while (q->next != NULL
8041 && q->next->offset < p->offset) {
8042 q = q->next;
8043 }
8044 p->next = q->next;
8045 q->next = p;
8046 } else {
8047 p->next = q;
8048 result[anchor->line_num] = p;
8049 }
8050 } else {
8051 result[anchor->line_num] = p;
8052 }
8053 break;
8054 }
8055 }
8056 }
8057 }
8058 return result;
8059 }
8060
8061 /*
8062 * Free the data allocated in allocAnchorIndex().
8063 */
freeAnchorIndex(AnchorIndex ** inx,unsigned inx_size)8064 static void freeAnchorIndex(AnchorIndex ** inx, unsigned inx_size)
8065 {
8066 AnchorIndex *cur;
8067 unsigned num;
8068
8069 if (inx != 0) {
8070 if (inx_size != 0) {
8071 for (num = 0; num < inx_size; ++num) {
8072 while ((cur = inx[num]) != NULL) {
8073 inx[num] = cur->next;
8074 free(cur);
8075 }
8076 }
8077 }
8078 free(inx);
8079 }
8080 }
8081
8082 /*
8083 * Return the column (counting from zero) at which a field should be overlaid
8084 * on the form.
8085 */
FieldFirst(AnchorIndex * p,int wrap)8086 static int FieldFirst(AnchorIndex * p, int wrap)
8087 {
8088 return (wrap ? 0 : (p)->offset);
8089 }
8090
8091 /*
8092 * Return the column (counting from zero) just past the field in a form.
8093 */
FieldLast(AnchorIndex * p,int wrap)8094 static int FieldLast(AnchorIndex * p, int wrap)
8095 {
8096 return ((p)->size - wrap) + FieldFirst(p, wrap);
8097 }
8098
8099 /*
8100 * Print the contents of the file in HTMainText to
8101 * the file descriptor fp.
8102 * If is_email is TRUE add ">" before each "From " line.
8103 * If is_reply is TRUE add ">" to the beginning of each
8104 * line to specify the file is a reply to message.
8105 */
print_wwwfile_to_fd(FILE * fp,int is_email,int is_reply)8106 void print_wwwfile_to_fd(FILE *fp,
8107 int is_email,
8108 int is_reply)
8109 {
8110 int line_num, byte_num, byte_count, byte_next, byte_offset;
8111 int first = TRUE;
8112 HTLine *line;
8113 AnchorIndex **inx; /* sorted index of input-fields */
8114 AnchorIndex *cur = 0; /* current input-field */
8115 unsigned inx_size; /* number of entries in inx[] */
8116 int in_field = -1; /* if positive, is index in cur->value[] */
8117 int this_wrap = 0; /* current wrapping point of cur->value[] */
8118 int next_wrap = 0; /* next wrapping point of cur->value[] */
8119
8120 #ifndef NO_DUMP_WITH_BACKSPACES
8121 HText *text = HTMainText;
8122 BOOL in_b = FALSE;
8123 BOOL in_u = FALSE;
8124 BOOL bs = (BOOL) (!is_email && !is_reply
8125 && text != 0
8126 && with_backspaces
8127 && !IS_CJK_TTY
8128 && !text->T.output_utf8);
8129 #endif
8130
8131 if (!HTMainText)
8132 return;
8133
8134 /*
8135 * Build an index of anchors for each line, so we can override the
8136 * static text which is stored in the list of HTLine's.
8137 */
8138 inx = allocAnchorIndex(&inx_size);
8139
8140 line = FirstHTLine(HTMainText);
8141 for (line_num = 0;; ++line_num, line = line->next) {
8142 if (in_field >= 0) {
8143 this_wrap = next_wrap;
8144 next_wrap = 0; /* FIXME - allow for multiple continuations */
8145 CTRACE2(TRACE_GRIDTEXT,
8146 (tfp, "wrap %d:%d, offset %d\n",
8147 in_field, cur ? cur->length : -1, this_wrap));
8148 } else {
8149 cur = inx[line_num];
8150 }
8151
8152 CTRACE2(TRACE_GRIDTEXT, (tfp, "dump %d:%s\n", line_num, line->data));
8153
8154 if (first) {
8155 first = FALSE;
8156 if (is_reply) {
8157 fputc('>', fp);
8158 } else if (is_email && !StrNCmp(line->data, "From ", 5)) {
8159 fputc('>', fp);
8160 }
8161 } else if (line->data[0] != LY_SOFT_NEWLINE) {
8162 fputc('\n', fp);
8163 /*
8164 * Add news-style quotation if requested. -FM
8165 */
8166 if (is_reply) {
8167 fputc('>', fp);
8168 } else if (is_email && !StrNCmp(line->data, "From ", 5)) {
8169 fputc('>', fp);
8170 }
8171 }
8172
8173 write_offset(fp, line);
8174
8175 /*
8176 * Add data.
8177 */
8178 byte_offset = line->offset;
8179 byte_count = TrimmedLength(line->data);
8180 byte_next = 1;
8181 for (byte_num = 0; byte_num < byte_count; byte_num += byte_next) {
8182 int cell_chr, temp_chr;
8183 size_t cell_len, temp_len;
8184 const char *cell_ptr, *temp_ptr, *try_utf8;
8185
8186 cell_ptr = &line->data[byte_num];
8187 cell_len = 1;
8188 cell_chr = UCH(*cell_ptr);
8189 byte_next = 1;
8190
8191 while (cur != 0 && FieldLast(cur, this_wrap) < byte_offset) {
8192 CTRACE2(TRACE_GRIDTEXT,
8193 (tfp, "skip field since last %d < %d\n",
8194 FieldLast(cur, this_wrap), byte_offset));
8195 cur = cur->next;
8196 in_field = -1;
8197 }
8198 if (cur != 0 && in_field >= 0) {
8199 CTRACE2(TRACE_GRIDTEXT,
8200 (tfp, "compare %d to [%d..%d]\n",
8201 byte_offset,
8202 FieldFirst(cur, this_wrap),
8203 FieldLast(cur, this_wrap) - 1));
8204 }
8205 if (cur != 0
8206 && FieldFirst(cur, this_wrap) <= byte_offset
8207 && FieldLast(cur, this_wrap) > byte_offset) {
8208 int off2 = ((in_field > 0)
8209 ? in_field
8210 : (byte_offset - FieldFirst(cur, this_wrap)));
8211
8212 /*
8213 * On the first time (for each line that the field appears on),
8214 * check if this field wraps. If it does, save the offset into
8215 * the field which will be used to adjust the beginning of the
8216 * continuation line.
8217 */
8218 if (byte_offset == FieldFirst(cur, this_wrap)) {
8219 next_wrap = 0;
8220 if (cur->size - this_wrap + byte_num > byte_count) {
8221 CTRACE((tfp, "size %d, offset %d, length %d\n",
8222 cur->size,
8223 cur->offset,
8224 cur->length));
8225 CTRACE((tfp, "byte_count %d, byte_num %d\n",
8226 byte_count, byte_num));
8227 next_wrap = byte_count - byte_num;
8228 CTRACE2(TRACE_GRIDTEXT,
8229 (tfp, "field will wrap: %d\n", next_wrap));
8230 }
8231 }
8232
8233 if (off2 >= 0 && off2 < cur->length) {
8234 temp_ptr = &(cur->value[off2]);
8235 try_utf8 = temp_ptr;
8236 temp_chr = (int) UCGetUniFromUtf8String(&try_utf8);
8237 if (temp_chr > 127) {
8238 temp_len = (size_t) (try_utf8 - temp_ptr) + 1;
8239 } else {
8240 temp_chr = UCH(*temp_ptr);
8241 temp_len = 1;
8242 }
8243 } else {
8244 temp_ptr = &(cur->filler);
8245 temp_len = 1;
8246 temp_chr = UCH(*temp_ptr);
8247 }
8248
8249 if (cell_chr != temp_chr) {
8250 CTRACE2(TRACE_GRIDTEXT,
8251 (tfp, "line %d %d/%d [%d..%d] map %d %04X->%04X\n",
8252 line_num,
8253 off2, cur->length,
8254 FieldFirst(cur, this_wrap),
8255 FieldLast(cur, this_wrap) - 1,
8256 byte_offset, cell_chr, temp_chr));
8257 cell_chr = temp_chr;
8258 cell_ptr = temp_ptr;
8259 cell_len = temp_len;
8260 }
8261 off2 += (int) temp_len;
8262 byte_offset += (int) temp_len;
8263 if ((off2 >= cur->size) &&
8264 (off2 >= cur->length || F_TEXTLIKE(cur->type))) {
8265 in_field = -1;
8266 this_wrap = 0;
8267 next_wrap = 0;
8268 } else {
8269 in_field = off2;
8270 }
8271 } else {
8272 byte_offset++;
8273 }
8274
8275 if (!IsSpecialAttrChar(cell_chr)) {
8276 #ifndef NO_DUMP_WITH_BACKSPACES
8277 size_t n;
8278
8279 if (in_b) {
8280 IGNORE_RC(fwrite(cell_ptr, sizeof(char), cell_len, fp));
8281
8282 for (n = 0; n < cell_len; ++n) {
8283 fputc('\b', fp);
8284 }
8285 IGNORE_RC(fwrite(cell_ptr, sizeof(char), cell_len, fp));
8286 } else if (in_u) {
8287 for (n = 0; n < cell_len; ++n) {
8288 fputc('_', fp);
8289 }
8290 for (n = 0; n < cell_len; ++n) {
8291 fputc('\b', fp);
8292 }
8293 IGNORE_RC(fwrite(cell_ptr, sizeof(char), cell_len, fp));
8294 } else
8295 #endif
8296 IGNORE_RC(fwrite(cell_ptr, sizeof(char), cell_len, fp));
8297 } else if (cell_chr == LY_SOFT_HYPHEN &&
8298 (byte_num + 1) >= byte_count) {
8299 write_hyphen(fp);
8300 } else if (dump_output_immediately && use_underscore) {
8301 switch (cell_chr) {
8302 case LY_UNDERLINE_START_CHAR:
8303 case LY_UNDERLINE_END_CHAR:
8304 fputc('_', fp);
8305 break;
8306 case LY_BOLD_START_CHAR:
8307 case LY_BOLD_END_CHAR:
8308 break;
8309 }
8310 }
8311 #ifndef NO_DUMP_WITH_BACKSPACES
8312 else if (bs) {
8313 switch (cell_chr) {
8314 case LY_UNDERLINE_START_CHAR:
8315 if (!in_b)
8316 in_u = TRUE; /*favor bold over underline */
8317 break;
8318 case LY_UNDERLINE_END_CHAR:
8319 in_u = FALSE;
8320 break;
8321 case LY_BOLD_START_CHAR:
8322 if (in_u)
8323 in_u = FALSE; /* turn it off */
8324 in_b = TRUE;
8325 break;
8326 case LY_BOLD_END_CHAR:
8327 in_b = FALSE;
8328 break;
8329 }
8330 }
8331 #endif
8332 }
8333
8334 if (line == HTMainText->last_line)
8335 break;
8336
8337 #ifdef VMS
8338 if (HadVMSInterrupt)
8339 break;
8340 #endif /* VMS */
8341 }
8342 fputc('\n', fp);
8343
8344 freeAnchorIndex(inx, inx_size);
8345 }
8346
8347 /*
8348 * Print the contents of the file in HTMainText to
8349 * the file descriptor fp.
8350 * First output line is "thelink", ie, the URL for this file.
8351 */
print_crawl_to_fd(FILE * fp,char * thelink,char * thetitle)8352 void print_crawl_to_fd(FILE *fp, char *thelink,
8353 char *thetitle)
8354 {
8355 register int i;
8356 int first = TRUE;
8357 int limit;
8358 HTLine *line;
8359
8360 if (!HTMainText)
8361 return;
8362
8363 line = FirstHTLine(HTMainText);
8364 fprintf(fp, "THE_URL:%s\n", thelink);
8365 if (thetitle != NULL) {
8366 fprintf(fp, "THE_TITLE:%s\n", thetitle);
8367 }
8368
8369 for (;; line = line->next) {
8370 if (!first && line->data[0] != LY_SOFT_NEWLINE)
8371 fputc('\n', fp);
8372 first = FALSE;
8373 write_offset(fp, line);
8374
8375 /*
8376 * Add data.
8377 */
8378 limit = TrimmedLength(line->data);
8379 for (i = 0; i < limit; i++) {
8380 int ch = UCH(line->data[i]);
8381
8382 if (!IsSpecialAttrChar(ch)) {
8383 fputc(ch, fp);
8384 } else if (ch == LY_SOFT_HYPHEN &&
8385 (i + 1) >= limit) { /* last char on line */
8386 write_hyphen(fp);
8387 }
8388 }
8389
8390 if (!HTMainText || (line == HTMainText->last_line)) {
8391 break;
8392 }
8393 }
8394 fputc('\n', fp);
8395
8396 /*
8397 * Add the References list if appropriate
8398 */
8399 if ((no_list == FALSE) &&
8400 (dump_links_inline == FALSE) &&
8401 links_are_numbered()) {
8402 printlist(fp, FALSE);
8403 }
8404 #ifdef VMS
8405 HadVMSInterrupt = FALSE;
8406 #endif /* VMS */
8407 }
8408
adjust_search_result(DocInfo * doc,int tentative_result,int start_line)8409 static void adjust_search_result(DocInfo *doc, int tentative_result,
8410 int start_line)
8411 {
8412 if (tentative_result > 0) {
8413 int anch_line = -1;
8414 TextAnchor *a;
8415 int nl_closest = -1;
8416 int goal = SEARCH_GOAL_LINE;
8417 int max_offset;
8418 BOOL on_screen = (BOOL) (tentative_result > HTMainText->top_of_screen &&
8419 tentative_result <= HTMainText->top_of_screen +
8420 display_lines);
8421
8422 if (goal < 1)
8423 goal = 1;
8424 else if (goal > display_lines)
8425 goal = display_lines;
8426 max_offset = goal - 1;
8427
8428 if (on_screen && nlinks > 0) {
8429 int i;
8430
8431 for (i = 0; i < nlinks; i++) {
8432 if (doc->line + links[i].ly - 1 <= tentative_result)
8433 nl_closest = i;
8434 if (doc->line + links[i].ly - 1 >= tentative_result)
8435 break;
8436 }
8437 if (nl_closest >= 0 &&
8438 doc->line + links[nl_closest].ly - 1 == tentative_result) {
8439 www_search_result = doc->line;
8440 doc->link = nl_closest;
8441 return;
8442 }
8443 }
8444
8445 /* find last anchor before or on target line */
8446 for (a = HTMainText->first_anchor;
8447 a && a->line_num <= tentative_result - 1; a = a->next) {
8448 anch_line = a->line_num + 1;
8449 }
8450 /* position such that the anchor found is on first screen line,
8451 if it is not too far above the target line; but also try to
8452 make sure we move forward. */
8453 if (anch_line >= 0 &&
8454 anch_line >= tentative_result - max_offset &&
8455 (anch_line > start_line ||
8456 tentative_result <= HTMainText->top_of_screen)) {
8457 www_search_result = anch_line;
8458 } else if (tentative_result - start_line > 0 &&
8459 tentative_result - (start_line + 1) <= max_offset) {
8460 www_search_result = start_line + 1;
8461 } else if (tentative_result > HTMainText->top_of_screen &&
8462 tentative_result <= start_line && /* have wrapped */
8463 tentative_result <= HTMainText->top_of_screen + goal) {
8464 www_search_result = HTMainText->top_of_screen + 1;
8465 } else if (tentative_result <= goal)
8466 www_search_result = 1;
8467 else
8468 www_search_result = tentative_result - max_offset;
8469 if (www_search_result == doc->line) {
8470 if (nl_closest >= 0) {
8471 doc->link = nl_closest;
8472 return;
8473 }
8474 }
8475 }
8476 }
8477
8478 /*
8479 * see also link_has_target
8480 */
anchor_has_target(TextAnchor * a,char * target)8481 static BOOL anchor_has_target(TextAnchor *a, char *target)
8482 {
8483 char *text = NULL;
8484 const char *last = "?";
8485 int count;
8486
8487 /*
8488 * Combine the parts of the link's text using the highlighting information,
8489 * and compare the target against that.
8490 */
8491 for (count = 0; count < 10; ++count) {
8492 const char *part = LYGetHiTextStr(a, count);
8493
8494 if (part == NULL || part == last) {
8495 if (text != NULL && LYno_attr_strstr(text, target)) {
8496 return TRUE;
8497 }
8498 break;
8499 }
8500 StrAllocCat(text, part);
8501 last = part;
8502 }
8503
8504 return field_has_target(a->input_field, target);
8505 }
8506
line_num_to_anchor(int line_num)8507 static TextAnchor *line_num_to_anchor(int line_num)
8508 {
8509 TextAnchor *a;
8510
8511 if (HTMainText != 0) {
8512 a = HTMainText->first_anchor;
8513 while (a != 0 && a->line_num < line_num) {
8514 a = a->next;
8515 }
8516 } else {
8517 a = 0;
8518 }
8519 return a;
8520 }
8521
line_num_in_text(HText * text,HTLine * line)8522 static int line_num_in_text(HText *text, HTLine *line)
8523 {
8524 int result = 1;
8525 HTLine *temp = FirstHTLine(text);
8526
8527 while (temp != line) {
8528 temp = temp->next;
8529 ++result;
8530 }
8531 return result;
8532 }
8533
8534 /* Computes the 'prev' pointers on demand, and returns the one for the given
8535 * anchor.
8536 */
get_prev_anchor(TextAnchor * a)8537 static TextAnchor *get_prev_anchor(TextAnchor *a)
8538 {
8539 TextAnchor *p, *q;
8540
8541 if (a->prev == 0) {
8542 if ((p = HTMainText->first_anchor) != 0) {
8543 while ((q = p->next) != 0) {
8544 q->prev = p;
8545 p = q;
8546 }
8547 }
8548 }
8549 return a->prev;
8550 }
8551
www_search_forward(int start_line,DocInfo * doc,char * target,HTLine * line,int count)8552 static int www_search_forward(int start_line,
8553 DocInfo *doc,
8554 char *target,
8555 HTLine *line,
8556 int count)
8557 {
8558 int wrapped = 0;
8559 TextAnchor *a = line_num_to_anchor(count - 1);
8560 int tentative_result = -1;
8561
8562 for (;;) {
8563 while ((a != NULL) && a->line_num == (count - 1)) {
8564 if (a->show_anchor &&
8565 !(a->link_type == INPUT_ANCHOR
8566 && a->input_field->type == F_HIDDEN_TYPE)) {
8567 if (anchor_has_target(a, target)) {
8568 adjust_search_result(doc, count, start_line);
8569 return 1;
8570 }
8571 }
8572 a = a->next;
8573 }
8574
8575 if (LYno_attr_strstr(line->data, target)) {
8576 tentative_result = count;
8577 break;
8578 } else if ((count == start_line && wrapped) || wrapped > 1) {
8579 HTUserMsg2(STRING_NOT_FOUND, target);
8580 return -1;
8581 } else if (line == HTMainText->last_line) {
8582 count = 0;
8583 wrapped++;
8584 a = HTMainText->first_anchor;
8585 }
8586 line = line->next;
8587 count++;
8588 }
8589 if (tentative_result > 0) {
8590 adjust_search_result(doc, tentative_result, start_line);
8591 }
8592 return 0;
8593 }
8594
www_search_backward(int start_line,DocInfo * doc,char * target,HTLine * line,int count)8595 static int www_search_backward(int start_line,
8596 DocInfo *doc,
8597 char *target,
8598 HTLine *line,
8599 int count)
8600 {
8601 int wrapped = 0;
8602 TextAnchor *a = line_num_to_anchor(count - 1);
8603 int tentative_result = -1;
8604
8605 for (;;) {
8606 while ((a != NULL) && a->line_num == (count - 1)) {
8607 if (a->show_anchor &&
8608 !(a->link_type == INPUT_ANCHOR
8609 && a->input_field->type == F_HIDDEN_TYPE)) {
8610 if (anchor_has_target(a, target)) {
8611 adjust_search_result(doc, count, start_line);
8612 return 1;
8613 }
8614 }
8615 a = get_prev_anchor(a);
8616 }
8617
8618 if (LYno_attr_strstr(line->data, target)) {
8619 tentative_result = count;
8620 break;
8621 } else if ((count == start_line && wrapped) || wrapped > 1) {
8622 HTUserMsg2(STRING_NOT_FOUND, target);
8623 return -1;
8624 } else if (line == FirstHTLine(HTMainText)) {
8625 count = line_num_in_text(HTMainText, LastHTLine(HTMainText)) + 1;
8626 wrapped++;
8627 a = HTMainText->last_anchor;
8628 }
8629 line = line->prev;
8630 count--;
8631 }
8632 if (tentative_result > 0) {
8633 adjust_search_result(doc, tentative_result, start_line);
8634 }
8635 return 0;
8636 }
8637
www_user_search(int start_line,DocInfo * doc,char * target,int direction)8638 void www_user_search(int start_line,
8639 DocInfo *doc,
8640 char *target,
8641 int direction)
8642 {
8643 HTLine *line;
8644 int count;
8645
8646 if (!HTMainText) {
8647 return;
8648 }
8649
8650 /*
8651 * Advance to the start line.
8652 */
8653 line = FirstHTLine(HTMainText);
8654 if (start_line + direction > 0) {
8655 for (count = 1;
8656 count < start_line + direction;
8657 line = line->next, count++) {
8658 if (line == HTMainText->last_line) {
8659 line = FirstHTLine(HTMainText);
8660 count = 1;
8661 break;
8662 }
8663 }
8664 } else {
8665 line = HTMainText->last_line;
8666 count = line_num_in_text(HTMainText, line);
8667 }
8668
8669 if (direction >= 0)
8670 www_search_forward(start_line, doc, target, line, count);
8671 else
8672 www_search_backward(start_line, doc, target, line, count);
8673 }
8674
user_message(const char * message,const char * argument)8675 void user_message(const char *message,
8676 const char *argument)
8677 {
8678 if (message == NULL) {
8679 mustshow = FALSE;
8680 } else {
8681 char *temp = NULL;
8682
8683 HTSprintf0(&temp, message, NonNull(argument));
8684 statusline(temp);
8685 FREE(temp);
8686 }
8687 }
8688
8689 /*
8690 * HText_getOwner returns the owner of the
8691 * current document.
8692 */
HText_getOwner(void)8693 const char *HText_getOwner(void)
8694 {
8695 return (HTMainText ?
8696 HTAnchor_owner(HTMainText->node_anchor) : 0);
8697 }
8698
8699 /*
8700 * HText_setMainTextOwner sets the owner for the
8701 * current document.
8702 */
HText_setMainTextOwner(const char * owner)8703 void HText_setMainTextOwner(const char *owner)
8704 {
8705 if (!HTMainText)
8706 return;
8707
8708 HTAnchor_setOwner(HTMainText->node_anchor, owner);
8709 }
8710
8711 /*
8712 * HText_getRevTitle returns the RevTitle element of the
8713 * current document, used as the subject for mailto comments
8714 * to the owner.
8715 */
HText_getRevTitle(void)8716 const char *HText_getRevTitle(void)
8717 {
8718 return (HTMainText ?
8719 HTAnchor_RevTitle(HTMainText->node_anchor) : 0);
8720 }
8721
8722 /*
8723 * HText_getContentBase returns the Content-Base header
8724 * of the current document.
8725 */
HText_getContentBase(void)8726 const char *HText_getContentBase(void)
8727 {
8728 return (HTMainText ?
8729 HTAnchor_content_base(HTMainText->node_anchor) : 0);
8730 }
8731
8732 /*
8733 * HText_getContentLocation returns the Content-Location header
8734 * of the current document.
8735 */
HText_getContentLocation(void)8736 const char *HText_getContentLocation(void)
8737 {
8738 return (HTMainText ?
8739 HTAnchor_content_location(HTMainText->node_anchor) : 0);
8740 }
8741
8742 /*
8743 * HText_getMessageID returns the Message-ID of the
8744 * current document.
8745 */
HText_getMessageID(void)8746 const char *HText_getMessageID(void)
8747 {
8748 return (HTMainText ?
8749 HTAnchor_messageID(HTMainText->node_anchor) : NULL);
8750 }
8751
HTuncache_current_document(void)8752 void HTuncache_current_document(void)
8753 {
8754 /*
8755 * Should remove current document from memory.
8756 */
8757 if (HTMainText) {
8758 HTParentAnchor *htmain_anchor = HTMainText->node_anchor;
8759
8760 if (htmain_anchor) {
8761 if (!(HTOutputFormat && HTOutputFormat == WWW_SOURCE)) {
8762 FREE(htmain_anchor->UCStages);
8763 }
8764 }
8765 CTRACE((tfp, "\nHTuncache.. freeing document for '%s'%s\n",
8766 ((htmain_anchor &&
8767 htmain_anchor->address) ?
8768 htmain_anchor->address : "unknown anchor"),
8769 ((htmain_anchor &&
8770 htmain_anchor->post_data)
8771 ? " with POST data"
8772 : "")));
8773 HTList_removeObject(loaded_texts, HTMainText);
8774 HText_free(HTMainText);
8775 HTMainText = NULL;
8776 } else {
8777 CTRACE((tfp, "HTuncache.. HTMainText already is NULL!\n"));
8778 }
8779 }
8780
8781 /*
8782 * This magic FREE(anchor->UCStages) call
8783 * stolen from HTuncache_current_document() above.
8784 */
magicUncache(void)8785 static void magicUncache(void)
8786 {
8787 if (!(HTOutputFormat && HTOutputFormat == WWW_SOURCE)) {
8788 FREE(HTMainAnchor->UCStages);
8789 }
8790 /* avoid null-reference later */
8791 if (!HTOutputFormat)
8792 HTOutputFormat = WWW_SOURCE;
8793 }
8794
8795 #ifdef USE_SOURCE_CACHE
8796
8797 /* dummy - kw */
8798 static HTProtocol scm =
8799 {
8800 "source-cache-mem", 0, 0
8801 };
8802
useSourceCache(void)8803 static BOOLEAN useSourceCache(void)
8804 {
8805 BOOLEAN result = FALSE;
8806
8807 if (LYCacheSource == SOURCE_CACHE_FILE) {
8808 result = (BOOLEAN) (HTMainAnchor->source_cache_file != 0);
8809 CTRACE((tfp, "SourceCache: file-cache%s found\n",
8810 result ? "" : " not"));
8811 }
8812 return result;
8813 }
8814
useMemoryCache(void)8815 static BOOLEAN useMemoryCache(void)
8816 {
8817 BOOLEAN result = FALSE;
8818
8819 if (LYCacheSource == SOURCE_CACHE_MEMORY) {
8820 result = (BOOLEAN) (HTMainAnchor->source_cache_chunk != 0);
8821 CTRACE((tfp, "SourceCache: memory-cache%s found\n",
8822 result ? "" : " not"));
8823 }
8824 return result;
8825 }
8826
HTreparse_document(void)8827 BOOLEAN HTreparse_document(void)
8828 {
8829 BOOLEAN ok = FALSE;
8830
8831 if (!HTMainAnchor || LYCacheSource == SOURCE_CACHE_NONE) {
8832 CTRACE((tfp, "HTreparse_document returns FALSE\n"));
8833 } else if (useSourceCache()) {
8834 FILE *fp;
8835 HTFormat format;
8836 int ret;
8837
8838 CTRACE((tfp, "SourceCache: Reparsing file %s\n",
8839 HTMainAnchor->source_cache_file));
8840
8841 magicUncache();
8842
8843 /*
8844 * This is more or less copied out of HTLoadFile(), except we don't
8845 * get a content encoding. This may be overkill. -dsb
8846 */
8847 if (HTMainAnchor->content_type) {
8848 format = HTAtom_for(HTMainAnchor->content_type);
8849 } else {
8850 format = HTFileFormat(HTMainAnchor->source_cache_file, NULL, NULL);
8851 format = HTCharsetFormat(format, HTMainAnchor,
8852 UCLYhndl_for_unspec);
8853 /* not UCLYhndl_HTFile_for_unspec - we are talking about remote
8854 * documents...
8855 */
8856 }
8857 CTRACE((tfp, " Content type is \"%s\"\n", format->name));
8858
8859 fp = fopen(HTMainAnchor->source_cache_file, "r");
8860 if (!fp) {
8861 CTRACE((tfp, " Cannot read file %s\n", HTMainAnchor->source_cache_file));
8862 (void) LYRemoveTemp(HTMainAnchor->source_cache_file);
8863 FREE(HTMainAnchor->source_cache_file);
8864 } else {
8865
8866 if (HText_HaveUserChangedForms(HTMainText)) {
8867 /*
8868 * Issue a warning. Will not restore changed forms, currently.
8869 */
8870 HTAlert(RELOADING_FORM);
8871 }
8872 /* Set HTMainAnchor->protocol or HTMainAnchor->physical to convince
8873 * the SourceCacheWriter to not regenerate the cache file (which
8874 * would be an unnecessary "loop"). - kw
8875 */
8876 HTAnchor_setProtocol(HTMainAnchor, &HTFile);
8877 ret = HTParseFile(format, HTOutputFormat, HTMainAnchor, fp, NULL);
8878 LYCloseInput(fp);
8879 if (ret == HT_PARTIAL_CONTENT) {
8880 HTInfoMsg(gettext("Loading incomplete."));
8881 CTRACE((tfp,
8882 "SourceCache: `%s' has been accessed, partial content.\n",
8883 HTLoadedDocumentURL()));
8884 }
8885 ok = (BOOL) (ret == HT_LOADED || ret == HT_PARTIAL_CONTENT);
8886
8887 CTRACE((tfp, "Reparse file %s\n", (ok ? "succeeded" : "failed")));
8888 }
8889 } else if (useMemoryCache()) {
8890 HTFormat format = WWW_HTML;
8891 int ret;
8892
8893 CTRACE((tfp, "SourceCache: Reparsing from memory chunk %p\n",
8894 (void *) HTMainAnchor->source_cache_chunk));
8895
8896 magicUncache();
8897
8898 if (HTMainAnchor->content_type) {
8899 format = HTAtom_for(HTMainAnchor->content_type);
8900 } else {
8901 /*
8902 * This is only done to make things aligned with SOURCE_CACHE_NONE
8903 * and SOURCE_CACHE_FILE when switching to source mode since the
8904 * original document's charset will be LYPushAssumed() and then
8905 * LYPopAssumed(). See LYK_SOURCE in mainloop if you change
8906 * something here. No user-visible benefits, seems just '=' Info
8907 * Page will show source's effective charset as "(assumed)".
8908 */
8909 format = HTCharsetFormat(format, HTMainAnchor,
8910 UCLYhndl_for_unspec);
8911 }
8912 /* not UCLYhndl_HTFile_for_unspec - we are talking about remote documents... */
8913
8914 if (HText_HaveUserChangedForms(HTMainText)) {
8915 /*
8916 * Issue a warning. Will not restore changed forms, currently.
8917 */
8918 HTAlert(RELOADING_FORM);
8919 }
8920 /* Set HTMainAnchor->protocol or HTMainAnchor->physical to convince
8921 * the SourceCacheWriter to not regenerate the cache chunk (which
8922 * would be an unnecessary "loop"). - kw
8923 */
8924 HTAnchor_setProtocol(HTMainAnchor, &scm); /* cheating -
8925 anything != &HTTP or &HTTPS would do - kw */
8926 ret = HTParseMem(format, HTOutputFormat, HTMainAnchor,
8927 HTMainAnchor->source_cache_chunk, NULL);
8928 ok = (BOOL) (ret == HT_LOADED);
8929
8930 CTRACE((tfp, "Reparse memory %s\n", (ok ? "succeeded" : "failed")));
8931 }
8932
8933 return ok;
8934 }
8935
HTcan_reparse_document(void)8936 BOOLEAN HTcan_reparse_document(void)
8937 {
8938 BOOLEAN result = FALSE;
8939
8940 if (!HTMainAnchor || LYCacheSource == SOURCE_CACHE_NONE) {
8941 result = FALSE;
8942 } else if (useSourceCache()) {
8943 result = LYCanReadFile(HTMainAnchor->source_cache_file);
8944 } else if (useMemoryCache()) {
8945 result = TRUE;
8946 }
8947
8948 CTRACE((tfp, "HTcan_reparse_document -> %d\n", result));
8949 return result;
8950 }
8951
trace_setting_change(const char * name,int prev_setting,int new_setting)8952 static void trace_setting_change(const char *name,
8953 int prev_setting,
8954 int new_setting)
8955 {
8956 if (prev_setting != new_setting)
8957 CTRACE((tfp,
8958 "HTdocument_settings_changed: %s setting has changed (was %d, now %d)\n",
8959 name, prev_setting, new_setting));
8960 }
8961
HTdocument_settings_changed(void)8962 BOOLEAN HTdocument_settings_changed(void)
8963 {
8964 /*
8965 * Annoying Hack(TM): If we don't have a source cache, we can't
8966 * reparse anyway, so pretend the settings haven't changed.
8967 */
8968 if (!HTMainText || !HTcan_reparse_document())
8969 return FALSE;
8970
8971 if (TRACE) {
8972 /*
8973 * If we're tracing, note everying that has changed.
8974 */
8975 trace_setting_change("CLICKABLE_IMAGES",
8976 HTMainText->clickable_images, clickable_images);
8977 trace_setting_change("PSEUDO_INLINE_ALTS",
8978 HTMainText->pseudo_inline_alts,
8979 pseudo_inline_alts);
8980 trace_setting_change("VERBOSE_IMG",
8981 HTMainText->verbose_img,
8982 verbose_img);
8983 trace_setting_change("RAW_MODE", HTMainText->raw_mode,
8984 LYUseDefaultRawMode);
8985 trace_setting_change("HISTORICAL_COMMENTS",
8986 HTMainText->historical_comments,
8987 historical_comments);
8988 trace_setting_change("MINIMAL_COMMENTS",
8989 HTMainText->minimal_comments, minimal_comments);
8990 trace_setting_change("SOFT_DQUOTES",
8991 HTMainText->soft_dquotes, soft_dquotes);
8992 trace_setting_change("OLD_DTD", HTMainText->old_dtd, Old_DTD);
8993 trace_setting_change("KEYPAD_MODE",
8994 HTMainText->keypad_mode, keypad_mode);
8995 if (HTMainText->disp_lines != LYlines || HTMainText->disp_cols != DISPLAY_COLS)
8996 CTRACE((tfp,
8997 "HTdocument_settings_changed: Screen size has changed (was %dx%d, now %dx%d)\n",
8998 HTMainText->disp_cols,
8999 HTMainText->disp_lines,
9000 DISPLAY_COLS,
9001 LYlines));
9002 }
9003
9004 return (BOOLEAN) (HTMainText->clickable_images != clickable_images ||
9005 HTMainText->pseudo_inline_alts != pseudo_inline_alts ||
9006 HTMainText->verbose_img != verbose_img ||
9007 HTMainText->raw_mode != LYUseDefaultRawMode ||
9008 HTMainText->historical_comments != historical_comments ||
9009 (HTMainText->minimal_comments != minimal_comments &&
9010 !historical_comments) ||
9011 HTMainText->soft_dquotes != soft_dquotes ||
9012 HTMainText->old_dtd != Old_DTD ||
9013 HTMainText->keypad_mode != keypad_mode ||
9014 HTMainText->disp_cols != DISPLAY_COLS);
9015 }
9016 #endif
9017
HTisDocumentSource(void)9018 int HTisDocumentSource(void)
9019 {
9020 return (HTMainText != 0) ? HTMainText->source : FALSE;
9021 }
9022
HTLoadedDocumentURL(void)9023 const char *HTLoadedDocumentURL(void)
9024 {
9025 if (!HTMainText)
9026 return ("");
9027
9028 if (HTMainText->node_anchor && HTMainText->node_anchor->address)
9029 return (HTMainText->node_anchor->address);
9030 else
9031 return ("");
9032 }
9033
HTLoadedDocumentPost_data(void)9034 bstring *HTLoadedDocumentPost_data(void)
9035 {
9036 if (HTMainText
9037 && HTMainText->node_anchor
9038 && HTMainText->node_anchor->post_data)
9039 return (HTMainText->node_anchor->post_data);
9040 else
9041 return (0);
9042 }
9043
HTLoadedDocumentTitle(void)9044 const char *HTLoadedDocumentTitle(void)
9045 {
9046 if (!HTMainText)
9047 return ("");
9048
9049 if (HTMainText->node_anchor && HTMainText->node_anchor->title)
9050 return (HTMainText->node_anchor->title);
9051 else
9052 return ("");
9053 }
9054
HTLoadedDocumentIsHEAD(void)9055 BOOLEAN HTLoadedDocumentIsHEAD(void)
9056 {
9057 if (!HTMainText)
9058 return (FALSE);
9059
9060 if (HTMainText->node_anchor && HTMainText->node_anchor->isHEAD)
9061 return (HTMainText->node_anchor->isHEAD);
9062 else
9063 return (FALSE);
9064 }
9065
HTLoadedDocumentIsSafe(void)9066 BOOLEAN HTLoadedDocumentIsSafe(void)
9067 {
9068 if (!HTMainText)
9069 return (FALSE);
9070
9071 if (HTMainText->node_anchor && HTMainText->node_anchor->safe)
9072 return (HTMainText->node_anchor->safe);
9073 else
9074 return (FALSE);
9075 }
9076
HTLoadedDocumentCharset(void)9077 const char *HTLoadedDocumentCharset(void)
9078 {
9079 const char *result = NULL;
9080
9081 if (HTMainText &&
9082 HTMainText->node_anchor) {
9083 result = HTMainText->node_anchor->charset;
9084 }
9085
9086 return result;
9087 }
9088
HTLoadedDocumentEightbit(void)9089 BOOL HTLoadedDocumentEightbit(void)
9090 {
9091 if (!HTMainText)
9092 return (NO);
9093 else
9094 return (HTMainText->have_8bit_chars);
9095 }
9096
HText_setNodeAnchorBookmark(const char * bookmark)9097 void HText_setNodeAnchorBookmark(const char *bookmark)
9098 {
9099 if (!HTMainText)
9100 return;
9101
9102 if (HTMainText->node_anchor)
9103 HTAnchor_setBookmark(HTMainText->node_anchor, bookmark);
9104 }
9105
HTLoadedDocumentBookmark(void)9106 const char *HTLoadedDocumentBookmark(void)
9107 {
9108 if (!HTMainText)
9109 return (NULL);
9110
9111 if (HTMainText->node_anchor && HTMainText->node_anchor->bookmark)
9112 return (HTMainText->node_anchor->bookmark);
9113 else
9114 return (NULL);
9115 }
9116
HText_LastLineSize(HText * text,int IgnoreSpaces)9117 int HText_LastLineSize(HText *text, int IgnoreSpaces)
9118 {
9119 if (!text || !text->last_line || !text->last_line->size)
9120 return 0;
9121 return HText_TrueLineSize(text->last_line, text, IgnoreSpaces);
9122 }
9123
HText_LastLineEmpty(HText * text,int IgnoreSpaces)9124 BOOL HText_LastLineEmpty(HText *text, int IgnoreSpaces)
9125 {
9126 if (!text || !text->last_line || !text->last_line->size)
9127 return TRUE;
9128 return HText_TrueEmptyLine(text->last_line, text, IgnoreSpaces);
9129 }
9130
HText_LastLineOffset(HText * text)9131 int HText_LastLineOffset(HText *text)
9132 {
9133 if (!text || !text->last_line)
9134 return 0;
9135 return text->last_line->offset;
9136 }
9137
HText_PreviousLineSize(HText * text,int IgnoreSpaces)9138 int HText_PreviousLineSize(HText *text, int IgnoreSpaces)
9139 {
9140 HTLine *line;
9141
9142 if (!text || !text->last_line)
9143 return 0;
9144 if (!(line = text->last_line->prev))
9145 return 0;
9146 return HText_TrueLineSize(line, text, IgnoreSpaces);
9147 }
9148
HText_PreviousLineEmpty(HText * text,int IgnoreSpaces)9149 BOOL HText_PreviousLineEmpty(HText *text, int IgnoreSpaces)
9150 {
9151 HTLine *line;
9152
9153 if (!text || !text->last_line)
9154 return TRUE;
9155 if (!(line = text->last_line->prev))
9156 return TRUE;
9157 return HText_TrueEmptyLine(line, text, IgnoreSpaces);
9158 }
9159
9160 /*
9161 * Compute the "true" line size.
9162 */
HText_TrueLineSize(HTLine * line,HText * text,int IgnoreSpaces)9163 static int HText_TrueLineSize(HTLine *line, HText *text, int IgnoreSpaces)
9164 {
9165 size_t i;
9166 int true_size = 0;
9167
9168 if (!(line && line->size))
9169 return 0;
9170
9171 if (IgnoreSpaces) {
9172 for (i = 0; i < line->size; i++) {
9173 if (!IsSpecialAttrChar(UCH(line->data[i])) &&
9174 IS_UTF8_EXTRA(line->data[i]) &&
9175 !isspace(UCH(line->data[i])) &&
9176 UCH(line->data[i]) != HT_NON_BREAK_SPACE &&
9177 UCH(line->data[i]) != HT_EN_SPACE) {
9178 true_size++;
9179 }
9180 }
9181 } else {
9182 for (i = 0; i < line->size; i++) {
9183 if (!IsSpecialAttrChar(line->data[i]) &&
9184 IS_UTF8_EXTRA(line->data[i])) {
9185 true_size++;
9186 }
9187 }
9188 }
9189 return true_size;
9190 }
9191
9192 /*
9193 * Tell if the line is really empty. This is invoked much more often than
9194 * HText_TrueLineSize(), and most lines are not empty. So it is faster to
9195 * do this check than to check if the line size happens to be zero.
9196 */
HText_TrueEmptyLine(HTLine * line,HText * text,int IgnoreSpaces)9197 static BOOL HText_TrueEmptyLine(HTLine *line, HText *text, int IgnoreSpaces)
9198 {
9199 size_t i;
9200
9201 if (!(line && line->size))
9202 return TRUE;
9203
9204 if (IgnoreSpaces) {
9205 for (i = 0; i < line->size; i++) {
9206 if (!IsSpecialAttrChar(UCH(line->data[i])) &&
9207 IS_UTF8_EXTRA(line->data[i]) &&
9208 !isspace(UCH(line->data[i])) &&
9209 UCH(line->data[i]) != HT_NON_BREAK_SPACE &&
9210 UCH(line->data[i]) != HT_EN_SPACE) {
9211 return FALSE;
9212 }
9213 }
9214 } else {
9215 for (i = 0; i < line->size; i++) {
9216 if (!IsSpecialAttrChar(line->data[i]) &&
9217 IS_UTF8_EXTRA(line->data[i])) {
9218 return FALSE;
9219 }
9220 }
9221 }
9222 return TRUE;
9223 }
9224
HText_NegateLineOne(HText * text)9225 void HText_NegateLineOne(HText *text)
9226 {
9227 if (text) {
9228 text->in_line_1 = NO;
9229 }
9230 return;
9231 }
9232
HText_inLineOne(HText * text)9233 BOOL HText_inLineOne(HText *text)
9234 {
9235 if (text) {
9236 return text->in_line_1;
9237 }
9238 return YES;
9239 }
9240
9241 /*
9242 * This function is for removing the first of two
9243 * successive blank lines. It should be called after
9244 * checking the situation with HText_LastLineSize()
9245 * and HText_PreviousLineSize(). Any characters in
9246 * the removed line (i.e., control characters, or it
9247 * wouldn't have tested blank) should have been
9248 * reiterated by split_line() in the retained blank
9249 * line. -FM
9250 */
HText_RemovePreviousLine(HText * text)9251 void HText_RemovePreviousLine(HText *text)
9252 {
9253 HTLine *line, *previous;
9254
9255 if (!(text && text->Lines > 1))
9256 return;
9257
9258 line = text->last_line->prev;
9259 previous = line->prev;
9260 previous->next = text->last_line;
9261 text->last_line->prev = previous;
9262 text->Lines--;
9263 freeHTLine(text, line);
9264 }
9265
9266 /*
9267 * NOTE: This function presently is correct only if the
9268 * alignment is HT_LEFT. The offset is still zero,
9269 * because that's not determined for HT_CENTER or
9270 * HT_RIGHT until subsequent characters are received
9271 * and split_line() is called. -FM
9272 */
HText_getCurrentColumn(HText * text)9273 int HText_getCurrentColumn(HText *text)
9274 {
9275 int column = 0;
9276 BOOL IgnoreSpaces = FALSE;
9277
9278 if (text) {
9279 column = ((text->in_line_1
9280 ? (int) text->style->indent1st
9281 : (int) text->style->leftIndent)
9282 + (int) text->last_line->offset
9283 + HText_LastLineSize(text, IgnoreSpaces));
9284 }
9285 return column;
9286 }
9287
HText_getMaximumColumn(HText * text)9288 int HText_getMaximumColumn(HText *text)
9289 {
9290 int column = DISPLAY_COLS;
9291
9292 if (text) {
9293 column -= (int) text->style->rightIndent;
9294 }
9295 return column;
9296 }
9297
9298 /*
9299 * NOTE: This function uses HText_getCurrentColumn() which
9300 * presently is correct only if the alignment is
9301 * HT_LEFT. -FM
9302 */
HText_setTabID(HText * text,const char * name)9303 void HText_setTabID(HText *text, const char *name)
9304 {
9305 HTTabID *Tab = NULL;
9306 HTList *cur = text->tabs;
9307 HTList *last = NULL;
9308
9309 if (!text || isEmpty(name))
9310 return;
9311
9312 if (!cur) {
9313 cur = text->tabs = HTList_new();
9314 } else {
9315 while (NULL != (Tab = (HTTabID *) HTList_nextObject(cur))) {
9316 if (Tab->name && !strcmp(Tab->name, name))
9317 return; /* Already set. Keep the first value. */
9318 last = cur;
9319 }
9320 if (last)
9321 cur = last;
9322 }
9323 if (!Tab) { /* New name. Create a new node */
9324 Tab = typecalloc(HTTabID);
9325 if (Tab == NULL)
9326 outofmem(__FILE__, "HText_setTabID");
9327 HTList_addObject(cur, Tab);
9328 StrAllocCopy(Tab->name, name);
9329 }
9330
9331 Tab->column = HText_getCurrentColumn(text);
9332 return;
9333 }
9334
HText_getTabIDColumn(HText * text,const char * name)9335 int HText_getTabIDColumn(HText *text, const char *name)
9336 {
9337 int column = 0;
9338 HTTabID *Tab;
9339 HTList *cur = text->tabs;
9340
9341 if (text && non_empty(name) && cur) {
9342 while (NULL != (Tab = (HTTabID *) HTList_nextObject(cur))) {
9343 if (Tab->name && !strcmp(Tab->name, name))
9344 break;
9345 }
9346 if (Tab)
9347 column = Tab->column;
9348 }
9349 return column;
9350 }
9351
9352 /*
9353 * This function is for saving the address of a link
9354 * which had an attribute in the markup that resolved
9355 * to a URL (i.e., not just a NAME or ID attribute),
9356 * but was found in HText_endAnchor() to have no visible
9357 * content for use as a link name. It loads the address
9358 * into text->hidden_links, whose count can be determined
9359 * via HText_HiddenLinks(), below. The addresses can be
9360 * retrieved via HText_HiddenLinkAt(), below, based on
9361 * count. -FM
9362 */
HText_AddHiddenLink(HText * text,TextAnchor * textanchor)9363 static void HText_AddHiddenLink(HText *text, TextAnchor *textanchor)
9364 {
9365 HTAnchor *dest;
9366
9367 /*
9368 * Make sure we have an HText structure and anchor. -FM
9369 */
9370 if (!(text && textanchor && textanchor->anchor))
9371 return;
9372
9373 /*
9374 * Create the hidden links list
9375 * if it hasn't been already. -FM
9376 */
9377 if (text->hidden_links == NULL)
9378 text->hidden_links = HTList_new();
9379
9380 /*
9381 * Store the address, in reverse list order
9382 * so that first in will be first out on
9383 * retrievals. -FM
9384 */
9385 if ((dest = HTAnchor_followLink(textanchor->anchor)) &&
9386 (text->hiddenlinkflag != HIDDENLINKS_IGNORE ||
9387 HTList_isEmpty(text->hidden_links))) {
9388 char *value = HTAnchor_address(dest);
9389 BOOL ignore = FALSE;
9390
9391 if (unique_urls) {
9392 int cnt;
9393 char *check;
9394
9395 for (cnt = 0;; ++cnt) {
9396
9397 check = (char *) HTList_objectAt(text->hidden_links, cnt);
9398 if (check == 0)
9399 break;
9400 if (!strcmp(check, value)) {
9401 ignore = TRUE;
9402 break;
9403 }
9404 }
9405 }
9406 if (ignore) {
9407 FREE(value);
9408 } else {
9409 HTList_appendObject(text->hidden_links, value);
9410 }
9411 }
9412
9413 return;
9414 }
9415
9416 /*
9417 * This function returns the number of addresses
9418 * that are loaded in text->hidden_links. -FM
9419 */
HText_HiddenLinkCount(HText * text)9420 int HText_HiddenLinkCount(HText *text)
9421 {
9422 int count = 0;
9423
9424 if (text && text->hidden_links)
9425 count = HTList_count((HTList *) text->hidden_links);
9426
9427 return (count);
9428 }
9429
9430 /*
9431 * This function returns the address, corresponding to
9432 * a hidden link, at the position (zero-based) in the
9433 * text->hidden_links list of the number argument. -FM
9434 */
HText_HiddenLinkAt(HText * text,int number)9435 const char *HText_HiddenLinkAt(HText *text, int number)
9436 {
9437 char *href = NULL;
9438
9439 if (text && text->hidden_links && number >= 0)
9440 href = (char *) HTList_objectAt((HTList *) text->hidden_links, number);
9441
9442 return (href);
9443 }
9444
9445 /*
9446 * Form methods
9447 * These routines are used to build forms consisting
9448 * of input fields
9449 */
9450 static BOOLEAN HTFormDisabled = FALSE;
9451 static PerFormInfo *HTCurrentForm;
9452
addFormAction(FormInfo * f)9453 static BOOLEAN addFormAction(FormInfo * f)
9454 {
9455 BOOLEAN result = FALSE;
9456
9457 if (HTCurrentForm != NULL) {
9458 result = TRUE;
9459 f->submit_action = NULL;
9460 StrAllocCopy(f->submit_action, HTCurrentForm->data.submit_action);
9461 if (HTCurrentForm->data.submit_enctype != NULL)
9462 StrAllocCopy(f->submit_enctype, HTCurrentForm->data.submit_enctype);
9463 if (HTCurrentForm->data.submit_title != NULL)
9464 StrAllocCopy(f->submit_title, HTCurrentForm->data.submit_title);
9465 f->submit_method = HTCurrentForm->data.submit_method;
9466 }
9467 return result;
9468 }
9469
HText_beginForm(char * action,char * method,char * enctype,char * title,const char * accept_cs)9470 void HText_beginForm(char *action,
9471 char *method,
9472 char *enctype,
9473 char *title,
9474 const char *accept_cs)
9475 {
9476 PerFormInfo *newform;
9477 int HTFormMethod = URL_GET_METHOD;
9478 char *HTFormAction = NULL;
9479 char *HTFormEnctype = NULL;
9480 char *HTFormTitle = NULL;
9481 char *HTFormAcceptCharset = NULL;
9482
9483 HTFormNumber++;
9484
9485 HTFormFields = 0;
9486 HTFormDisabled = FALSE;
9487
9488 /*
9489 * Check the ACTION. -FM
9490 */
9491 if (action != NULL) {
9492 if (isMAILTO_URL(action)) {
9493 HTFormMethod = URL_MAIL_METHOD;
9494 }
9495 StrAllocCopy(HTFormAction, action);
9496 } else
9497 StrAllocCopy(HTFormAction, HTLoadedDocumentURL());
9498
9499 /*
9500 * Check the METHOD. -FM
9501 */
9502 if (method != NULL && HTFormMethod != URL_MAIL_METHOD)
9503 if (!strcasecomp(method, "post") || !strcasecomp(method, "pget"))
9504 HTFormMethod = URL_POST_METHOD;
9505
9506 /*
9507 * Check the ENCTYPE. -FM
9508 */
9509 if (non_empty(enctype)) {
9510 StrAllocCopy(HTFormEnctype, enctype);
9511 if (HTFormMethod != URL_MAIL_METHOD &&
9512 !strncasecomp(enctype, "multipart/form-data", 19))
9513 HTFormMethod = URL_POST_METHOD;
9514 } else {
9515 FREE(HTFormEnctype);
9516 }
9517
9518 /*
9519 * Check the TITLE. -FM
9520 */
9521 if (non_empty(title))
9522 StrAllocCopy(HTFormTitle, title);
9523 else
9524 FREE(HTFormTitle);
9525
9526 /*
9527 * Check for an ACCEPT_CHARSET. If present, store it and
9528 * convert to lowercase and collapse spaces. - kw
9529 */
9530 if (accept_cs != NULL) {
9531 StrAllocCopy(HTFormAcceptCharset, accept_cs);
9532 LYRemoveBlanks(HTFormAcceptCharset);
9533 LYLowerCase(HTFormAcceptCharset);
9534 }
9535
9536 /*
9537 * Create a new "PerFormInfo" structure to hold info on the current form.
9538 * This will be appended to the forms list kept by the HText object if and
9539 * when we reach a HText_endForm.
9540 */
9541 newform = typecalloc(PerFormInfo);
9542 if (newform == NULL)
9543 outofmem(__FILE__, "HText_beginForm");
9544
9545 PerFormInfo_free(HTCurrentForm); /* shouldn't happen here - kw */
9546 HTCurrentForm = newform;
9547
9548 newform->number = HTFormNumber;
9549 newform->data.submit_action = HTFormAction;
9550 newform->data.submit_enctype = HTFormEnctype;
9551 newform->data.submit_method = HTFormMethod;
9552 newform->data.submit_title = HTFormTitle;
9553 newform->accept_cs = HTFormAcceptCharset;
9554
9555 CTRACE((tfp, "BeginForm: action:%s Method:%d%s%s%s%s%s%s\n",
9556 HTFormAction, HTFormMethod,
9557 (HTFormTitle ? " Title:" : ""),
9558 NonNull(HTFormTitle),
9559 (HTFormEnctype ? " Enctype:" : ""),
9560 NonNull(HTFormEnctype),
9561 (HTFormAcceptCharset ? " Accept-charset:" : ""),
9562 NonNull(HTFormAcceptCharset)));
9563 }
9564
HText_endForm(HText * text)9565 void HText_endForm(HText *text)
9566 {
9567 if (text != NULL) {
9568 if (HTFormFields == 1 && text->first_anchor) {
9569 /*
9570 * Support submission of a single text input field in
9571 * the form via <return> instead of a submit button. -FM
9572 */
9573 TextAnchor *a;
9574
9575 /*
9576 * Go through list of anchors and get our input field. -FM
9577 */
9578 for (a = text->first_anchor; a != NULL; a = a->next) {
9579 if (a->link_type == INPUT_ANCHOR &&
9580 a->input_field->number == HTFormNumber &&
9581 a->input_field->type != F_TEXTAREA_TYPE &&
9582 F_TEXTLIKE(a->input_field->type)) {
9583 /*
9584 * Got it. Make it submitting. -FM
9585 */
9586 if (addFormAction(a->input_field)) {
9587 a->input_field->type = F_TEXT_SUBMIT_TYPE;
9588 if (HTFormDisabled)
9589 a->input_field->disabled = TRUE;
9590 }
9591 break;
9592 }
9593 }
9594 }
9595
9596 /*
9597 * Append info on the current form to the HText object's list of forms.
9598 * HText_beginInput call will have set some of the data in the
9599 * PerFormInfo structure (if there were any form fields at all).
9600 */
9601 if (HTCurrentForm) {
9602 if (HTFormDisabled)
9603 HTCurrentForm->disabled = TRUE;
9604 if (!text->forms)
9605 text->forms = HTList_new();
9606 HTList_appendObject(text->forms, HTCurrentForm);
9607 HTCurrentForm = NULL;
9608 } else {
9609 CTRACE((tfp, "endForm: HTCurrentForm is missing!\n"));
9610 }
9611 } else {
9612 CTRACE((tfp, "endForm: HText is missing!\n"));
9613 }
9614
9615 FREE(HTCurSelectGroup);
9616 FREE(HTCurSelectGroupSize);
9617 FREE(HTCurSelectedOptionValue);
9618 HTFormFields = 0;
9619 HTFormDisabled = FALSE;
9620 }
9621
HText_beginSelect(char * name,int name_cs,int multiple,char * size)9622 void HText_beginSelect(char *name,
9623 int name_cs,
9624 int multiple,
9625 char *size)
9626 {
9627 /*
9628 * Save the group name.
9629 */
9630 StrAllocCopy(HTCurSelectGroup, name);
9631 HTCurSelectGroupCharset = name_cs;
9632
9633 /*
9634 * If multiple then all options are actually checkboxes.
9635 */
9636 if (multiple)
9637 HTCurSelectGroupType = F_CHECKBOX_TYPE;
9638 /*
9639 * If not multiple then all options are radio buttons.
9640 */
9641 else
9642 HTCurSelectGroupType = F_RADIO_TYPE;
9643
9644 /*
9645 * Length of an option list.
9646 */
9647 StrAllocCopy(HTCurSelectGroupSize, size);
9648
9649 CTRACE((tfp, "HText_beginSelect: name=%s type=%d size=%s\n",
9650 ((HTCurSelectGroup == NULL) ?
9651 "<NULL>" : HTCurSelectGroup),
9652 HTCurSelectGroupType,
9653 ((HTCurSelectGroupSize == NULL) ?
9654 "<NULL>" : HTCurSelectGroupSize)));
9655 CTRACE((tfp, "HText_beginSelect: name_cs=%d \"%s\"\n",
9656 HTCurSelectGroupCharset,
9657 (HTCurSelectGroupCharset >= 0 ?
9658 LYCharSet_UC[HTCurSelectGroupCharset].MIMEname : "<UNKNOWN>")));
9659 }
9660
9661 /*
9662 * This function returns the number of the option whose
9663 * value currently is being accumulated for a select
9664 * block. - LE && FM
9665 */
HText_getOptionNum(HText * text)9666 int HText_getOptionNum(HText *text)
9667 {
9668 TextAnchor *a;
9669 OptionType *op;
9670 int n = 1; /* start count at 1 */
9671
9672 if (!(text && text->last_anchor))
9673 return (0);
9674
9675 a = text->last_anchor;
9676 if (!(a->link_type == INPUT_ANCHOR && a->input_field &&
9677 a->input_field->type == F_OPTION_LIST_TYPE))
9678 return (0);
9679
9680 for (op = a->input_field->select_list; op; op = op->next)
9681 n++;
9682 CTRACE((tfp, "HText_getOptionNum: Got number '%d'.\n", n));
9683 return (n);
9684 }
9685
9686 /*
9687 * This function checks for a numbered option pattern
9688 * as the prefix for an option value. If present, and
9689 * we are in the correct keypad mode, it returns a
9690 * pointer to the actual value, following that prefix.
9691 * Otherwise, it returns the original pointer.
9692 */
HText_skipOptionNumPrefix(char * opname)9693 static char *HText_skipOptionNumPrefix(char *opname)
9694 {
9695 /*
9696 * Check if we are in the correct keypad mode.
9697 */
9698 if (fields_are_numbered()) {
9699 /*
9700 * Skip the option number embedded in the option name so the
9701 * extra chars won't mess up cgi scripts processing the value.
9702 * The format is (nnn)__ where nnn is a number and there is a
9703 * minimum of 5 chars (no underscores if (nnn) exceeds 5 chars).
9704 * See HTML.c. If the chars don't exactly match this format,
9705 * just use all of opname. - LE
9706 */
9707 char *cp = opname;
9708
9709 if ((non_empty(cp) && *cp++ == '(') &&
9710 *cp && isdigit(UCH(*cp++))) {
9711 while (*cp && isdigit(UCH(*cp)))
9712 ++cp;
9713 if (*cp && *cp++ == ')') {
9714 int i = (int) (cp - opname);
9715
9716 while (i < 5) {
9717 if (*cp != '_')
9718 break;
9719 i++;
9720 cp++;
9721 }
9722 if (i < 5) {
9723 cp = opname;
9724 }
9725 } else {
9726 cp = opname;
9727 }
9728 } else {
9729 cp = opname;
9730 }
9731 return (cp);
9732 }
9733
9734 return (opname);
9735 }
9736
9737 /*
9738 * We couldn't set the value field for the previous option tag so we have to do
9739 * it now. Assume that the last anchor was the previous options' tag.
9740 */
HText_setLastOptionValue(HText * text,char * value,char * submit_value,int order,int checked,int val_cs,int submit_val_cs)9741 char *HText_setLastOptionValue(HText *text, char *value,
9742 char *submit_value,
9743 int order,
9744 int checked,
9745 int val_cs,
9746 int submit_val_cs)
9747 {
9748 char *cp, *cp1;
9749 char *ret_Value = NULL;
9750 unsigned char *tmp = NULL;
9751 int number = 0, i, j;
9752
9753 if (!(value
9754 && text
9755 && text->last_anchor
9756 && text->last_anchor->input_field
9757 && text->last_anchor->link_type == INPUT_ANCHOR)) {
9758 CTRACE((tfp, "HText_setLastOptionValue: invalid call! value:%s!\n",
9759 (value ? value : "<NULL>")));
9760 return NULL;
9761 }
9762
9763 CTRACE((tfp,
9764 "Entering HText_setLastOptionValue: value:\"%s\", checked:%s\n",
9765 value, (checked ? "on" : "off")));
9766
9767 /*
9768 * Strip end spaces, newline is also whitespace.
9769 */
9770 if (*value) {
9771 cp = &value[strlen(value) - 1];
9772 while ((cp >= value) && (isspace(UCH(*cp)) ||
9773 IsSpecialAttrChar(UCH(*cp))))
9774 cp--;
9775 *(cp + 1) = '\0';
9776 }
9777
9778 /*
9779 * Find first non space
9780 */
9781 cp = value;
9782 while (isspace(UCH(*cp)) ||
9783 IsSpecialAttrChar(UCH(*cp)))
9784 cp++;
9785 if (HTCurSelectGroupType == F_RADIO_TYPE &&
9786 LYSelectPopups &&
9787 fields_are_numbered()) {
9788 /*
9789 * Collapse any space between the popup option
9790 * prefix and actual value. -FM
9791 */
9792 if ((cp1 = HText_skipOptionNumPrefix(cp)) > cp) {
9793 i = 0, j = (int) (cp1 - cp);
9794 while (isspace(UCH(cp1[i])) ||
9795 IsSpecialAttrChar(UCH(cp1[i]))) {
9796 i++;
9797 }
9798 if (i > 0) {
9799 while (cp1[i] != '\0')
9800 cp[j++] = cp1[i++];
9801 cp[j] = '\0';
9802 }
9803 }
9804 }
9805
9806 if (HTCurSelectGroupType == F_CHECKBOX_TYPE) {
9807 StrAllocCopy(text->last_anchor->input_field->value, cp);
9808 text->last_anchor->input_field->value_cs = val_cs;
9809 /*
9810 * Put the text on the screen as well.
9811 */
9812 HText_appendText(text, cp);
9813
9814 } else if (LYSelectPopups == FALSE) {
9815 StrAllocCopy(text->last_anchor->input_field->value,
9816 (submit_value ? submit_value : cp));
9817 text->last_anchor->input_field->value_cs = (submit_value ?
9818 submit_val_cs : val_cs);
9819 /*
9820 * Put the text on the screen as well.
9821 */
9822 HText_appendText(text, cp);
9823
9824 } else {
9825 /*
9826 * Create a linked list of option values.
9827 */
9828 OptionType *op_ptr = text->last_anchor->input_field->select_list;
9829 OptionType *new_ptr = NULL;
9830 BOOLEAN first_option = FALSE;
9831
9832 /*
9833 * Deal with newlines or tabs.
9834 */
9835 LYReduceBlanks(value);
9836
9837 if (!op_ptr) {
9838 /*
9839 * No option items yet.
9840 */
9841 if (text->last_anchor->input_field->type != F_OPTION_LIST_TYPE) {
9842 CTRACE((tfp,
9843 "HText_setLastOptionValue: last input_field not F_OPTION_LIST_TYPE (%d)\n",
9844 F_OPTION_LIST_TYPE));
9845 CTRACE((tfp, " but %d, ignoring!\n",
9846 text->last_anchor->input_field->type));
9847 return NULL;
9848 }
9849
9850 new_ptr = typecalloc(OptionType);
9851 if (new_ptr == NULL)
9852 outofmem(__FILE__, "HText_setLastOptionValue");
9853
9854 text->last_anchor->input_field->select_list = new_ptr;
9855 first_option = TRUE;
9856 } else {
9857 while (op_ptr->next) {
9858 number++;
9859 op_ptr = op_ptr->next;
9860 }
9861 number++; /* add one more */
9862
9863 op_ptr->next = new_ptr = typecalloc(OptionType);
9864 if (new_ptr == NULL)
9865 outofmem(__FILE__, "HText_setLastOptionValue");
9866 }
9867
9868 new_ptr->name = NULL;
9869 new_ptr->cp_submit_value = NULL;
9870 new_ptr->next = NULL;
9871 /*
9872 * Find first non-space again, convert_to_spaces above may have
9873 * changed the string. - kw
9874 */
9875 cp = value;
9876 while (isspace(UCH(*cp)) ||
9877 IsSpecialAttrChar(UCH(*cp)))
9878 cp++;
9879 for (i = 0, j = 0; cp[i]; i++) {
9880 if (cp[i] == HT_NON_BREAK_SPACE ||
9881 cp[i] == HT_EN_SPACE) {
9882 cp[j++] = ' ';
9883 } else if (cp[i] != LY_SOFT_HYPHEN &&
9884 !IsSpecialAttrChar(UCH(cp[i]))) {
9885 cp[j++] = cp[i];
9886 }
9887 }
9888 cp[j] = '\0';
9889 if (IS_CJK_TTY) {
9890 if ((tmp = typecallocn(unsigned char, strlen(cp) * 2 + 1)) != 0) {
9891 if (kanji_code == EUC) {
9892 TO_EUC((unsigned char *) cp, tmp);
9893 val_cs = current_char_set;
9894 } else if (kanji_code == SJIS) {
9895 TO_SJIS((unsigned char *) cp, tmp);
9896 val_cs = current_char_set;
9897 } else {
9898 for (i = 0, j = 0; cp[i]; i++) {
9899 if (cp[i] != CH_ESC) { /* S/390 -- gil -- 1604 */
9900 tmp[j++] = UCH(cp[i]);
9901 }
9902 }
9903 }
9904 StrAllocCopy(new_ptr->name, (const char *) tmp);
9905 FREE(tmp);
9906 } else {
9907 outofmem(__FILE__, "HText_setLastOptionValue");
9908 }
9909 } else {
9910 StrAllocCopy(new_ptr->name, cp);
9911 }
9912 StrAllocCopy(new_ptr->cp_submit_value,
9913 (submit_value ? submit_value :
9914 HText_skipOptionNumPrefix(new_ptr->name)));
9915 new_ptr->value_cs = (submit_value ? submit_val_cs : val_cs);
9916
9917 if (first_option) {
9918 FormInfo *last_input = text->last_anchor->input_field;
9919
9920 StrAllocCopy(HTCurSelectedOptionValue, new_ptr->name);
9921 last_input->num_value = 0;
9922 /*
9923 * If this is the first option in a popup select list,
9924 * HText_beginInput may have allocated the value and
9925 * cp_submit_value fields, so free them now to avoid
9926 * a memory leak. - kw
9927 */
9928 FREE(last_input->value);
9929 FREE(last_input->cp_submit_value);
9930
9931 last_input->value = last_input->select_list->name;
9932 last_input->orig_value = last_input->select_list->name;
9933 last_input->cp_submit_value = last_input->select_list->cp_submit_value;
9934 last_input->orig_submit_value = last_input->select_list->cp_submit_value;
9935 last_input->value_cs = new_ptr->value_cs;
9936 } else {
9937 int newlen = (int) strlen(new_ptr->name);
9938 int curlen = (int) (HTCurSelectedOptionValue
9939 ? strlen(HTCurSelectedOptionValue)
9940 : 0);
9941
9942 /*
9943 * Make the selected Option Value as long as
9944 * the longest option.
9945 */
9946 if (newlen > curlen)
9947 StrAllocCat(HTCurSelectedOptionValue,
9948 UNDERSCORES(newlen - curlen));
9949 }
9950
9951 if (checked) {
9952 int curlen = (int) strlen(new_ptr->name);
9953 int newlen = (HTCurSelectedOptionValue
9954 ? (int) strlen(HTCurSelectedOptionValue)
9955 : 0);
9956 FormInfo *last_input = text->last_anchor->input_field;
9957
9958 /*
9959 * Set the default option as this one.
9960 */
9961 last_input->num_value = number;
9962 last_input->value = new_ptr->name;
9963 last_input->orig_value = new_ptr->name;
9964 last_input->cp_submit_value = new_ptr->cp_submit_value;
9965 last_input->orig_submit_value = new_ptr->cp_submit_value;
9966 last_input->value_cs = new_ptr->value_cs;
9967 StrAllocCopy(HTCurSelectedOptionValue, new_ptr->name);
9968 if (newlen > curlen)
9969 StrAllocCat(HTCurSelectedOptionValue,
9970 UNDERSCORES(newlen - curlen));
9971 }
9972
9973 /*
9974 * Return the selected Option value to be sent to the screen.
9975 */
9976 if (order == LAST_ORDER) {
9977 /*
9978 * Change the value.
9979 */
9980 if (HTCurSelectedOptionValue == 0)
9981 StrAllocCopy(HTCurSelectedOptionValue, "");
9982 text->last_anchor->input_field->size =
9983 (int) strlen(HTCurSelectedOptionValue);
9984 ret_Value = HTCurSelectedOptionValue;
9985 }
9986 }
9987
9988 if (TRACE) {
9989 CTRACE((tfp, "HText_setLastOptionValue:%s value=\"%s\"\n",
9990 (order == LAST_ORDER) ? " LAST_ORDER" : "",
9991 value));
9992 CTRACE((tfp, " val_cs=%d \"%s\"",
9993 val_cs,
9994 (val_cs >= 0 ?
9995 LYCharSet_UC[val_cs].MIMEname : "<UNKNOWN>")));
9996 if (submit_value) {
9997 CTRACE((tfp, " (submit_val_cs %d \"%s\") submit_value%s=\"%s\"\n",
9998 submit_val_cs,
9999 (submit_val_cs >= 0 ?
10000 LYCharSet_UC[submit_val_cs].MIMEname : "<UNKNOWN>"),
10001 (HTCurSelectGroupType == F_CHECKBOX_TYPE) ?
10002 "(ignored)" : "",
10003 submit_value));
10004 } else {
10005 CTRACE((tfp, "\n"));
10006 }
10007 }
10008 return (ret_Value);
10009 }
10010
10011 /*
10012 * Assign a form input anchor.
10013 * Returns the number of characters to leave
10014 * blank so that the input field can fit.
10015 */
HText_beginInput(HText * text,int underline,InputFieldData * I)10016 int HText_beginInput(HText *text,
10017 int underline,
10018 InputFieldData * I)
10019 {
10020 TextAnchor *a;
10021 FormInfo *f;
10022 const char *cp_option = NULL;
10023 char *IValue = NULL;
10024 unsigned char *tmp = NULL;
10025 int i, j;
10026 int adjust_marker = 0;
10027 int MaximumSize;
10028 char marker[16];
10029
10030 CTRACE((tfp, "GridText: Entering HText_beginInput type=%s\n", NonNull(I->type)));
10031
10032 POOLtypecalloc(TextAnchor, a);
10033
10034 POOLtypecalloc(FormInfo, f);
10035 if (a == NULL || f == NULL)
10036 outofmem(__FILE__, "HText_beginInput");
10037
10038 a->sgml_offset = SGML_offset();
10039 a->inUnderline = (BOOLEAN) underline;
10040 a->line_num = text->Lines;
10041 a->line_pos = (short) text->last_line->size;
10042
10043 /*
10044 * If this is a radio button, or an OPTION we're converting
10045 * to a radio button, and it's the first with this name, make
10046 * sure it's checked by default. Otherwise, if it's checked,
10047 * uncheck the default or any preceding radio button with this
10048 * name that was checked. -FM
10049 */
10050 if (I->type != NULL && !strcmp(I->type, "OPTION") &&
10051 HTCurSelectGroupType == F_RADIO_TYPE && LYSelectPopups == FALSE) {
10052 I->type = "RADIO";
10053 I->name = HTCurSelectGroup;
10054 I->name_cs = HTCurSelectGroupCharset;
10055 }
10056 if (I->name && I->type && !strcasecomp(I->type, "radio")) {
10057 if (!text->last_anchor) {
10058 I->checked = TRUE;
10059 } else {
10060 TextAnchor *b;
10061 int i2 = 0;
10062
10063 for (b = text->first_anchor; b != NULL; b = b->next) {
10064 if (b->link_type == INPUT_ANCHOR &&
10065 b->input_field->type == F_RADIO_TYPE &&
10066 b->input_field->number == HTFormNumber) {
10067 if (!strcmp(b->input_field->name, I->name)) {
10068 if (I->checked && b->input_field->num_value) {
10069 b->input_field->num_value = 0;
10070 StrAllocCopy(b->input_field->orig_value, "0");
10071 break;
10072 }
10073 i2++;
10074 }
10075 }
10076 }
10077 if (i2 == 0)
10078 I->checked = TRUE;
10079 }
10080 }
10081
10082 a->next = 0;
10083 a->anchor = NULL;
10084 a->link_type = INPUT_ANCHOR;
10085 a->show_anchor = YES;
10086
10087 LYClearHiText(a);
10088 a->extent = 2;
10089
10090 a->input_field = f;
10091
10092 f->select_list = 0;
10093 f->number = HTFormNumber;
10094 f->disabled = HTFormDisabled || I->disabled;
10095 f->readonly = I->readonly;
10096 f->no_cache = NO;
10097
10098 HTFormFields++;
10099
10100 /*
10101 * Set up VALUE.
10102 */
10103 if (I->value) {
10104 StrAllocCopy(IValue, I->value);
10105 }
10106 if (IValue &&
10107 IS_CJK_TTY &&
10108 ((I->type == NULL) || strcasecomp(I->type, "hidden"))) {
10109 if ((tmp = typecallocn(unsigned char, strlen(IValue) * 2 + 1)) != 0) {
10110 if (kanji_code == EUC) {
10111 TO_EUC((unsigned char *) IValue, tmp);
10112 I->value_cs = current_char_set;
10113 } else if (kanji_code == SJIS) {
10114 TO_SJIS((unsigned char *) IValue, tmp);
10115 I->value_cs = current_char_set;
10116 } else {
10117 for (i = 0, j = 0; IValue[i]; i++) {
10118 if (IValue[i] != CH_ESC) { /* S/390 -- gil -- 1621 */
10119 tmp[j++] = UCH(IValue[i]);
10120 }
10121 }
10122 }
10123 StrAllocCopy(IValue, (const char *) tmp);
10124 FREE(tmp);
10125 }
10126 }
10127
10128 /*
10129 * Special case of OPTION.
10130 * Is handled above if radio type and LYSelectPopups is FALSE.
10131 */
10132 /* set the values and let the parsing below do the work */
10133 if (I->type != NULL && !strcmp(I->type, "OPTION")) {
10134 cp_option = I->type;
10135 if (HTCurSelectGroupType == F_RADIO_TYPE)
10136 I->type = "OPTION_LIST";
10137 else
10138 I->type = "CHECKBOX";
10139 I->name = HTCurSelectGroup;
10140 I->name_cs = HTCurSelectGroupCharset;
10141
10142 /*
10143 * The option's size parameter actually gives the length and not
10144 * the width of the list. Perform the conversion here
10145 * and get rid of the allocated HTCurSelect....
10146 * 0 is ok as it means any length (arbitrary decision).
10147 */
10148 if (HTCurSelectGroupSize != NULL) {
10149 f->size_l = atoi(HTCurSelectGroupSize);
10150 FREE(HTCurSelectGroupSize);
10151 }
10152 }
10153
10154 /*
10155 * Set SIZE.
10156 */
10157 if (I->size != 0) {
10158 f->size = I->size;
10159 /*
10160 * Leave at zero for option lists.
10161 */
10162 if (f->size == 0 && cp_option == NULL) {
10163 f->size = 20; /* default */
10164 }
10165 } else {
10166 f->size = 20; /* default */
10167 }
10168
10169 /*
10170 * Set MAXLENGTH.
10171 */
10172 if (I->maxlength != NULL) {
10173 f->maxlength = (unsigned) atoi(I->maxlength);
10174 } else {
10175 f->maxlength = 0; /* 0 means infinite */
10176 }
10177
10178 /*
10179 * Set CHECKED
10180 * (num_value is only relevant to check and radio types).
10181 */
10182 if (I->checked == TRUE)
10183 f->num_value = 1;
10184 else
10185 f->num_value = 0;
10186
10187 /*
10188 * Set TYPE.
10189 */
10190 if (I->type != NULL) {
10191 if (!strcasecomp(I->type, "password")) {
10192 f->type = F_PASSWORD_TYPE;
10193 } else if (!strcasecomp(I->type, "checkbox")) {
10194 f->type = F_CHECKBOX_TYPE;
10195 } else if (!strcasecomp(I->type, "radio")) {
10196 f->type = F_RADIO_TYPE;
10197 } else if (!strcasecomp(I->type, "submit")) {
10198 f->type = F_SUBMIT_TYPE;
10199 } else if (!strcasecomp(I->type, "image")) {
10200 f->type = F_IMAGE_SUBMIT_TYPE;
10201 } else if (!strcasecomp(I->type, "reset")) {
10202 f->type = F_RESET_TYPE;
10203 } else if (!strcasecomp(I->type, "OPTION_LIST")) {
10204 f->type = F_OPTION_LIST_TYPE;
10205 } else if (!strcasecomp(I->type, "hidden")) {
10206 f->type = F_HIDDEN_TYPE;
10207 HTFormFields--;
10208 f->size = 0;
10209 } else if (!strcasecomp(I->type, "textarea")) {
10210 f->type = F_TEXTAREA_TYPE;
10211 } else if (!strcasecomp(I->type, "range")) {
10212 f->type = F_RANGE_TYPE;
10213 } else if (!strcasecomp(I->type, "file")) {
10214 f->type = F_FILE_TYPE;
10215 CTRACE((tfp, "ok, got a file uploader\n"));
10216 } else if (!strcasecomp(I->type, "keygen")) {
10217 f->type = F_KEYGEN_TYPE;
10218 } else if (!strcasecomp(I->type, "button")) {
10219 f->type = F_BUTTON_TYPE;
10220 } else {
10221 /*
10222 * Note that TYPE="scribble" defaults to TYPE="text". -FM
10223 */
10224 f->type = F_TEXT_TYPE; /* default */
10225 }
10226 } else {
10227 f->type = F_TEXT_TYPE;
10228 }
10229
10230 /*
10231 * Set NAME.
10232 */
10233 if (I->name != NULL) {
10234 StrAllocCopy(f->name, I->name);
10235 f->name_cs = I->name_cs;
10236 } else {
10237 if (f->type == F_RESET_TYPE ||
10238 f->type == F_SUBMIT_TYPE ||
10239 f->type == F_IMAGE_SUBMIT_TYPE) {
10240 /*
10241 * Set name to empty string.
10242 */
10243 StrAllocCopy(f->name, "");
10244 } else {
10245 /*
10246 * Error! NAME must be present.
10247 */
10248 CTRACE((tfp,
10249 "GridText: No name present in input field; not displaying\n"));
10250 FREE(IValue);
10251 return (0);
10252 }
10253 }
10254
10255 /*
10256 * Add this anchor to the anchor list
10257 */
10258 if (text->last_anchor) {
10259 text->last_anchor->next = a;
10260 } else {
10261 text->first_anchor = a;
10262 }
10263
10264 /*
10265 * Set VALUE, if it exists. Otherwise, if it's not
10266 * an option list make it a zero-length string. -FM
10267 */
10268 if (IValue != NULL) {
10269 /*
10270 * OPTION VALUE is not actually the value to be seen but is to
10271 * be sent....
10272 */
10273 if (f->type == F_OPTION_LIST_TYPE ||
10274 f->type == F_CHECKBOX_TYPE) {
10275 /*
10276 * Fill both with the value. The f->value may be
10277 * overwritten in HText_setLastOptionValue....
10278 */
10279 StrAllocCopy(f->value, IValue);
10280 StrAllocCopy(f->cp_submit_value, IValue);
10281 } else {
10282 StrAllocCopy(f->value, IValue);
10283 }
10284 f->value_cs = I->value_cs;
10285 } else if (f->type != F_OPTION_LIST_TYPE) {
10286 StrAllocCopy(f->value, "");
10287 /*
10288 * May be an empty INPUT field. The text entered will then
10289 * probably be in the current display character set. - kw
10290 */
10291 f->value_cs = current_char_set;
10292 }
10293
10294 /*
10295 * Run checks and fill in necessary values.
10296 */
10297 if (f->type == F_RESET_TYPE) {
10298 if (non_empty(f->value)) {
10299 f->size = (int) strlen(f->value);
10300 } else {
10301 StrAllocCopy(f->value, "Reset");
10302 f->size = 5;
10303 }
10304 } else if (f->type == F_BUTTON_TYPE) {
10305 if (non_empty(f->value)) {
10306 f->size = (int) strlen(f->value);
10307 } else {
10308 StrAllocCopy(f->value, "BUTTON");
10309 f->size = 5;
10310 }
10311 } else if (f->type == F_IMAGE_SUBMIT_TYPE ||
10312 f->type == F_SUBMIT_TYPE) {
10313 if (non_empty(f->value)) {
10314 f->size = (int) strlen(f->value);
10315 } else if (f->type == F_IMAGE_SUBMIT_TYPE) {
10316 StrAllocCopy(f->value, "[IMAGE]-Submit");
10317 f->size = 14;
10318 } else {
10319 StrAllocCopy(f->value, "Submit");
10320 f->size = 6;
10321 }
10322 addFormAction(f);
10323 } else if (f->type == F_RADIO_TYPE || f->type == F_CHECKBOX_TYPE) {
10324 f->size = 3;
10325 if (IValue == NULL)
10326 StrAllocCopy(f->value, (f->type == F_CHECKBOX_TYPE ? "on" : ""));
10327
10328 }
10329 FREE(IValue);
10330
10331 /*
10332 * Set original values.
10333 */
10334 if (f->type == F_RADIO_TYPE || f->type == F_CHECKBOX_TYPE) {
10335 if (f->num_value)
10336 StrAllocCopy(f->orig_value, "1");
10337 else
10338 StrAllocCopy(f->orig_value, "0");
10339 } else if (f->type == F_OPTION_LIST_TYPE) {
10340 f->orig_value = NULL;
10341 } else {
10342 StrAllocCopy(f->orig_value, f->value);
10343 }
10344
10345 /*
10346 * Store accept-charset if present, converting to lowercase
10347 * and collapsing spaces. - kw
10348 */
10349 if (I->accept_cs) {
10350 StrAllocCopy(f->accept_cs, I->accept_cs);
10351 LYRemoveBlanks(f->accept_cs);
10352 LYLowerCase(f->accept_cs);
10353 }
10354
10355 /*
10356 * Add numbers to form fields if needed. - LE & FM
10357 */
10358 switch (f->type) {
10359 /*
10360 * Do not supply number for hidden fields, nor
10361 * for types that are not yet implemented.
10362 */
10363 case F_HIDDEN_TYPE:
10364 #ifndef USE_FILE_UPLOAD
10365 case F_FILE_TYPE:
10366 #endif
10367 case F_RANGE_TYPE:
10368 case F_KEYGEN_TYPE:
10369 case F_BUTTON_TYPE:
10370 a->number = 0;
10371 break;
10372
10373 default:
10374 if (fields_are_numbered())
10375 a->number = ++(text->last_anchor_number);
10376 else
10377 a->number = 0;
10378 break;
10379 }
10380 if (fields_are_numbered() && (a->number > 0)) {
10381 if (HTMainText != 0) {
10382 HText_findAnchorNumber(a);
10383 } else {
10384 a->show_number = a->number;
10385 }
10386 sprintf(marker, "[%d]", a->show_number);
10387 adjust_marker = (int) strlen(marker);
10388 if (number_fields_on_left) {
10389 BOOL had_bracket = (BOOL) (f->type == F_OPTION_LIST_TYPE);
10390
10391 HText_appendText(text, had_bracket ? (marker + 1) : marker);
10392 if (had_bracket)
10393 HText_appendCharacter(text, '[');
10394 }
10395 a->line_num = text->Lines;
10396 a->line_pos = (short) text->last_line->size;
10397 } else {
10398 *marker = '\0';
10399 }
10400
10401 /*
10402 * Restrict SIZE to maximum allowable size.
10403 */
10404 MaximumSize = WRAP_COLS(text) + 1 - adjust_marker;
10405 switch (f->type) {
10406
10407 case F_SUBMIT_TYPE:
10408 case F_IMAGE_SUBMIT_TYPE:
10409 case F_RESET_TYPE:
10410 case F_TEXT_TYPE:
10411 case F_TEXTAREA_TYPE:
10412 /*
10413 * For submit and reset buttons, and for text entry
10414 * fields and areas, we limit the size element to that
10415 * of one line for the current style because that's
10416 * the most we could highlight on overwrites, and/or
10417 * handle in the line editor. The actual values for
10418 * text entry lines can be long, and will be scrolled
10419 * horizontally within the editing window. -FM
10420 */
10421 MaximumSize -= (1 +
10422 (int) text->style->leftIndent +
10423 (int) text->style->rightIndent);
10424
10425 /* If we are numbering form links, place is taken by [nn] */
10426 if (fields_are_numbered()) {
10427 if (!number_fields_on_left
10428 && f->type == F_TEXT_TYPE
10429 && MaximumSize > a->line_pos + 10)
10430 MaximumSize -= a->line_pos;
10431 else
10432 MaximumSize -= (int) strlen(marker);
10433 }
10434
10435 /*
10436 * Save value for submit/reset buttons so they
10437 * will be visible when printing the page. - LE
10438 */
10439 if (f->type == F_SUBMIT_TYPE)
10440 FREE(I->value);
10441 I->value = f->value;
10442 break;
10443
10444 default:
10445 /*
10446 * For all other fields we limit the size element to
10447 * 10 less than the screen width, because either they
10448 * are types with small placeholders, and/or are a
10449 * type which is handled via a popup window. -FM
10450 */
10451 MaximumSize -= 10;
10452 break;
10453 }
10454
10455 if (MaximumSize < 1)
10456 MaximumSize = 1;
10457
10458 if (f->size > MaximumSize)
10459 f->size = MaximumSize;
10460
10461 /*
10462 * Add this anchor to the anchor list
10463 */
10464 text->last_anchor = a;
10465
10466 if (HTCurrentForm) { /* should always apply! - kw */
10467 if (!HTCurrentForm->first_field) {
10468 HTCurrentForm->first_field = f;
10469 }
10470 HTCurrentForm->last_field = f;
10471 HTCurrentForm->nfields++; /* will count hidden fields - kw */
10472 /*
10473 * Set the no_cache flag if the METHOD is POST. -FM
10474 */
10475 if (HTCurrentForm->data.submit_method == URL_POST_METHOD)
10476 f->no_cache = TRUE;
10477 /*
10478 * Propagate form field's accept-charset attribute to enclosing
10479 * form if the form itself didn't have an accept-charset - kw
10480 */
10481 if (f->accept_cs && !HTCurrentForm->accept_cs) {
10482 StrAllocCopy(HTCurrentForm->accept_cs, f->accept_cs);
10483 }
10484 if (!text->forms) {
10485 text->forms = HTList_new();
10486 }
10487 } else {
10488 CTRACE((tfp, "beginInput: HTCurrentForm is missing!\n"));
10489 }
10490
10491 CTRACE((tfp, "Input link: name=%s\nvalue=%s\nsize=%d\n",
10492 f->name,
10493 NonNull(f->value),
10494 f->size));
10495 CTRACE((tfp, "Input link: name_cs=%d \"%s\" (from %d \"%s\")\n",
10496 f->name_cs,
10497 (f->name_cs >= 0 ?
10498 LYCharSet_UC[f->name_cs].MIMEname : "<UNKNOWN>"),
10499 I->name_cs,
10500 (I->name_cs >= 0 ?
10501 LYCharSet_UC[I->name_cs].MIMEname : "<UNKNOWN>")));
10502 CTRACE((tfp, " value_cs=%d \"%s\" (from %d \"%s\")\n",
10503 f->value_cs,
10504 (f->value_cs >= 0 ?
10505 LYCharSet_UC[f->value_cs].MIMEname : "<UNKNOWN>"),
10506 I->value_cs,
10507 (I->value_cs >= 0 ?
10508 LYCharSet_UC[I->value_cs].MIMEname : "<UNKNOWN>")));
10509
10510 /*
10511 * Return the SIZE of the input field.
10512 */
10513 if (I->size && f->size > adjust_marker) {
10514 f->size -= adjust_marker;
10515 }
10516 return (f->size);
10517 }
10518
10519 /*
10520 * If we're numbering fields on the right, do it. Note that some fields may
10521 * be too long for the line - we'll lose the marker in that case rather than
10522 * truncate the field.
10523 */
HText_endInput(HText * text)10524 void HText_endInput(HText *text)
10525 {
10526 if (fields_are_numbered()
10527 && !number_fields_on_left
10528 && text != NULL
10529 && text->last_anchor != NULL
10530 && text->last_anchor->number > 0) {
10531 char marker[20];
10532
10533 sprintf(marker, "[%d]", text->last_anchor->show_number);
10534 HText_appendText(text, marker);
10535 }
10536 }
10537
10538 /*
10539 * Get a translation (properly: transcoding) quality, factoring in
10540 * our ability to translate (an UCTQ_t) and a possible q parameter
10541 * on the given charset string, for cs_from -> givenmime.
10542 * The parsed input string will be mutilated on exit(!).
10543 * Note that results are not normalised to 1.0, but results from
10544 * different calls of this function can be compared. - kw
10545 *
10546 * Obsolete, it was planned to use here a quality parametr UCTQ_t,
10547 * which is boolean now.
10548 */
get_trans_q(int cs_from,char * givenmime)10549 static double get_trans_q(int cs_from,
10550 char *givenmime)
10551 {
10552 double df = 1.0;
10553 BOOL tq;
10554 char *p;
10555
10556 if (!givenmime || !(*givenmime))
10557 return 0.0;
10558 if ((p = StrChr(givenmime, ';')) != NULL) {
10559 *p++ = '\0';
10560 }
10561 if (!strcmp(givenmime, "*"))
10562 tq = UCCanTranslateFromTo(cs_from,
10563 UCGetLYhndl_byMIME("utf-8"));
10564 else
10565 tq = UCCanTranslateFromTo(cs_from,
10566 UCGetLYhndl_byMIME(givenmime));
10567 if (!tq)
10568 return 0.0;
10569 if (non_empty(p)) {
10570 char *pair, *field = p, *pval, *ptok;
10571
10572 /* Get all the parameters to the Charset */
10573 while ((pair = HTNextTok(&field, ";", "\"", NULL)) != NULL) {
10574 if ((ptok = HTNextTok(&pair, "= ", NULL, NULL)) != NULL &&
10575 (pval = HTNextField(&pair)) != NULL) {
10576 if (0 == strcasecomp(ptok, "q")) {
10577 df = strtod(pval, NULL);
10578 break;
10579 }
10580 }
10581 }
10582 return (df * tq);
10583 } else {
10584 return tq;
10585 }
10586 }
10587
10588 /*
10589 * Find the best charset for submission, if we have an ACCEPT_CHARSET
10590 * list. It factors in how well we can translate (just as guess, and
10591 * not a very good one..) and possible ";q=" factors. Yes this is
10592 * more general than it needs to be here.
10593 *
10594 * Input is cs_in and acceptstring.
10595 *
10596 * Will return charset handle as int.
10597 * best_csname will point to a newly allocated MIME string for the
10598 * charset corresponding to the return value if return value >= 0.
10599 * - kw
10600 */
find_best_target_cs(char ** best_csname,int cs_from,const char * acceptstring)10601 static int find_best_target_cs(char **best_csname,
10602 int cs_from,
10603 const char *acceptstring)
10604 {
10605 char *paccept = NULL;
10606 double bestq = -1.0;
10607 char *bestmime = NULL;
10608 char *field, *nextfield;
10609
10610 StrAllocCopy(paccept, acceptstring);
10611 nextfield = paccept;
10612 while ((field = HTNextTok(&nextfield, ",", "\"", NULL)) != NULL) {
10613 double q;
10614
10615 if (*field != '\0') {
10616 /* Get the Charset */
10617 q = get_trans_q(cs_from, field);
10618 if (q > bestq) {
10619 bestq = q;
10620 bestmime = field;
10621 }
10622 }
10623 }
10624 if (bestmime) {
10625 if (!strcmp(bestmime, "*")) /* non-standard for HTML attribute.. */
10626 StrAllocCopy(*best_csname, "utf-8");
10627 else
10628 StrAllocCopy(*best_csname, bestmime);
10629 FREE(paccept);
10630 if (bestq > 0)
10631 return (UCGetLYhndl_byMIME(*best_csname));
10632 else
10633 return (-1);
10634 }
10635 FREE(paccept);
10636 return (-1);
10637 }
10638
10639 #ifdef USE_FILE_UPLOAD
load_a_file(const char * val_used,bstring ** result)10640 static void load_a_file(const char *val_used,
10641 bstring **result)
10642 {
10643 FILE *fd;
10644 size_t bytes;
10645 char bfr[BUFSIZ + 1];
10646
10647 CTRACE((tfp, "Ok, about to convert \"%s\" to mime/thingy\n", val_used));
10648
10649 if (*val_used) { /* ignore empty form field */
10650 if ((fd = fopen(val_used, BIN_R)) == 0) {
10651 HTAlert(gettext("Can't open file for uploading"));
10652 } else {
10653 while ((bytes = fread(bfr, sizeof(char), sizeof(bfr) - 1, fd)) != 0) {
10654 HTSABCat(result, bfr, (int) bytes);
10655 }
10656 LYCloseInput(fd);
10657 }
10658 }
10659 }
10660
guess_content_type(const char * filename)10661 static const char *guess_content_type(const char *filename)
10662 {
10663 HTAtom *encoding;
10664 const char *desc;
10665 HTFormat format = HTFileFormat(filename, &encoding, &desc);
10666
10667 return (format != 0 && non_empty(format->name))
10668 ? format->name
10669 : STR_PLAINTEXT;
10670 }
10671 #endif /* USE_FILE_UPLOAD */
10672
cannot_transcode(BOOL * had_warning,const char * target_csname)10673 static void cannot_transcode(BOOL *had_warning,
10674 const char *target_csname)
10675 {
10676 if (*had_warning == NO) {
10677 *had_warning = YES;
10678 _user_message(CANNOT_TRANSCODE_FORM,
10679 target_csname ? target_csname : "UNKNOWN");
10680 LYSleepAlert();
10681 }
10682 }
10683
10684 #define SPECIAL_8BIT 1
10685 #define SPECIAL_FORM 2
10686
check_form_specialchars(const char * value)10687 static unsigned check_form_specialchars(const char *value)
10688 {
10689 unsigned result = 0;
10690 const char *p;
10691
10692 for (p = value;
10693 non_empty(p) && (result != (SPECIAL_8BIT | SPECIAL_FORM));
10694 p++) {
10695 if ((*p == HT_NON_BREAK_SPACE) ||
10696 (*p == HT_EN_SPACE) ||
10697 (*p == LY_SOFT_HYPHEN)) {
10698 result |= SPECIAL_FORM;
10699 } else if ((*p & 0x80) != 0) {
10700 result |= SPECIAL_8BIT;
10701 }
10702 }
10703 return result;
10704 }
10705
10706 /*
10707 * Scan the given data, adding characters to the MIME-boundary to keep it from
10708 * matching any part of the data.
10709 */
UpdateBoundary(char ** Boundary,bstring * data)10710 static void UpdateBoundary(char **Boundary,
10711 bstring *data)
10712 {
10713 size_t j;
10714 size_t have = strlen(*Boundary);
10715 size_t last = (size_t) BStrLen(data);
10716 char *text = BStrData(data);
10717 char *want = *Boundary;
10718
10719 for (j = 0; (long) j <= (long) (last - have); ++j) {
10720 if (want[0] == text[j]
10721 && !memcmp(want, text + j, have)) {
10722 char temp[2];
10723
10724 temp[0] = (char) (isdigit(UCH(text[have + j])) ? 'a' : '0');
10725 temp[1] = '\0';
10726 StrAllocCat(want, temp);
10727 ++have;
10728 }
10729 }
10730 *Boundary = want;
10731 }
10732
10733 /*
10734 * Convert a string to base64
10735 */
convert_to_base64(const char * src,size_t len)10736 static char *convert_to_base64(const char *src,
10737 size_t len)
10738 {
10739 #define B64_LINE 76
10740
10741 static const char basis_64[] =
10742 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
10743
10744 char *dest;
10745 size_t rlen; /* length of result string */
10746 unsigned char c1, c2, c3;
10747 const char *eol;
10748 char *r;
10749 const char *str;
10750 size_t eollen;
10751 int chunk;
10752
10753 str = src;
10754 eol = "\n";
10755 eollen = 1;
10756
10757 /* calculate the length of the result */
10758 rlen = (len + 2) / 3 * 4; /* encoded bytes */
10759 if (rlen) {
10760 /* add space for EOL */
10761 rlen += ((rlen - 1) / B64_LINE + 1) * eollen;
10762 }
10763
10764 /* allocate a result buffer */
10765 if ((dest = (char *) malloc(rlen + 1)) == NULL) {
10766 outofmem(__FILE__, "convert_to_base64");
10767 }
10768 r = dest;
10769
10770 /* encode */
10771 for (chunk = 0; len > 0; len -= 3, chunk++) {
10772 if (chunk == (B64_LINE / 4)) {
10773 const char *c = eol;
10774 const char *e = eol + eollen;
10775
10776 while (c < e)
10777 *r++ = *c++;
10778 chunk = 0;
10779 }
10780 c1 = UCH(*str++);
10781 c2 = UCH(*str++);
10782 *r++ = basis_64[c1 >> 2];
10783 *r++ = basis_64[((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4)];
10784 if (len > 2) {
10785 c3 = UCH(*str++);
10786 *r++ = basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6)];
10787 *r++ = basis_64[c3 & 0x3F];
10788 } else if (len == 2) {
10789 *r++ = basis_64[(c2 & 0xF) << 2];
10790 *r++ = '=';
10791 } else { /* len == 1 */
10792 *r++ = '=';
10793 *r++ = '=';
10794 }
10795 }
10796 if (rlen) {
10797 /* append eol to the result string */
10798 const char *c = eol;
10799 const char *e = eol + eollen;
10800
10801 while (c < e)
10802 *r++ = *c++;
10803 }
10804 *r = '\0';
10805
10806 return dest;
10807 }
10808
10809 typedef enum {
10810 NO_QUOTE /* no quoting needed */
10811 ,QUOTE_MULTI /* multipart */
10812 ,QUOTE_BASE64 /* encode as base64 */
10813 ,QUOTE_SPECIAL /* escape special characters only */
10814 } QuoteData;
10815
10816 typedef struct {
10817 int type; /* the type of this field */
10818 BOOL first; /* true if this begins a submission part */
10819 char *name; /* the name of this field */
10820 char *value; /* the nominal value of this field */
10821 bstring *data; /* its data, which is usually the same as the value */
10822 QuoteData quote; /* how to quote/translate the data */
10823 } PostData;
10824
escape_or_quote_name(const char * name,QuoteData quoting,const char * MultipartContentType)10825 static char *escape_or_quote_name(const char *name,
10826 QuoteData quoting,
10827 const char *MultipartContentType)
10828 {
10829 char *escaped1 = NULL;
10830
10831 switch (quoting) {
10832 case NO_QUOTE:
10833 StrAllocCopy(escaped1, name);
10834 break;
10835 case QUOTE_MULTI:
10836 case QUOTE_BASE64:
10837 StrAllocCopy(escaped1, "Content-Disposition: form-data");
10838 HTSprintf(&escaped1, "; name=\"%s\"", name);
10839 if (MultipartContentType)
10840 HTSprintf(&escaped1, MultipartContentType, STR_PLAINTEXT);
10841 if (quoting == QUOTE_BASE64)
10842 StrAllocCat(escaped1, "\r\nContent-Transfer-Encoding: base64");
10843 StrAllocCat(escaped1, "\r\n\r\n");
10844 break;
10845 case QUOTE_SPECIAL:
10846 escaped1 = HTEscapeSP(name, URL_XALPHAS);
10847 break;
10848 }
10849 return escaped1;
10850 }
10851
escape_or_quote_value(const char * value,QuoteData quoting)10852 static char *escape_or_quote_value(const char *value,
10853 QuoteData quoting)
10854 {
10855 char *escaped2 = NULL;
10856
10857 switch (quoting) {
10858 case NO_QUOTE:
10859 case QUOTE_MULTI:
10860 StrAllocCopy(escaped2, NonNull(value));
10861 break;
10862 case QUOTE_BASE64:
10863 /* FIXME: this is redundant */
10864 escaped2 = convert_to_base64(value, strlen(value));
10865 break;
10866 case QUOTE_SPECIAL:
10867 escaped2 = HTEscapeSP(value, URL_XALPHAS);
10868 break;
10869 }
10870 return escaped2;
10871 }
10872
10873 /*
10874 * Check if we should encode the data in base64. We can, only if we're using
10875 * a multipart content type. We should, if we're sending mail and the data
10876 * contains long lines or nonprinting characters.
10877 */
check_if_base64_needed(int submit_method,bstring * data)10878 static int check_if_base64_needed(int submit_method,
10879 bstring *data)
10880 {
10881 int width = 0;
10882 BOOL printable = TRUE;
10883 char *text = BStrData(data);
10884
10885 if (text != 0) {
10886 int col = 0;
10887 int n;
10888 int length = BStrLen(data);
10889
10890 for (n = 0; n < length; ++n) {
10891 int ch = UCH(text[n]);
10892
10893 if (is8bits(ch) || ((ch < 32 && ch != '\n'))) {
10894 CTRACE((tfp, "nonprintable %d:%#x\n", n, ch));
10895 printable = FALSE;
10896 }
10897 if (ch == '\n' || ch == '\r') {
10898 if (width < col)
10899 width = col;
10900 col = 0;
10901 } else {
10902 ++col;
10903 }
10904 }
10905 if (width < col)
10906 width = col;
10907 }
10908 return !printable && ((submit_method == URL_MAIL_METHOD) && (width > 72));
10909 }
10910
HText_PerFormInfo(int number)10911 PerFormInfo *HText_PerFormInfo(int number)
10912 {
10913 return (PerFormInfo *) HTList_objectAt(HTMainText->forms, number - 1);
10914 }
10915
10916 /*
10917 * HText_SubmitForm - generate submit data from form fields.
10918 * For mailto forms, send the data.
10919 * For other methods, set fields in structure pointed to by doc
10920 * appropriately for next request.
10921 * Returns 1 if *doc set appropriately for next request,
10922 * 0 otherwise. - kw
10923 */
HText_SubmitForm(FormInfo * submit_item,DocInfo * doc,const char * link_name,const char * link_value)10924 int HText_SubmitForm(FormInfo * submit_item, DocInfo *doc,
10925 const char *link_name,
10926 const char *link_value)
10927 {
10928 BOOL had_chartrans_warning = NO;
10929 BOOL have_accept_cs = NO;
10930 BOOL success;
10931 BOOLEAN PlainText = FALSE;
10932 BOOLEAN SemiColon = FALSE;
10933 BOOL skip_field = FALSE;
10934 const char *out_csname;
10935 const char *target_csname = NULL;
10936 PerFormInfo *thisform;
10937 PostData *my_data = NULL;
10938 TextAnchor *anchor_ptr;
10939 bstring *my_query = NULL;
10940 char *Boundary = NULL;
10941 char *MultipartContentType = NULL;
10942 char *content_type_out = NULL;
10943 char *copied_name_used = NULL;
10944 char *copied_val_used = NULL;
10945 char *escaped1 = NULL;
10946 char *escaped2 = NULL;
10947 char *last_textarea_name = NULL;
10948 const char *name_used = "";
10949 char *previous_blanks = NULL;
10950 const char *val_used = "";
10951 int anchor_count = 0;
10952 int anchor_limit = 0;
10953 int form_number = submit_item->number;
10954 int result = 0;
10955 int target_cs = -1;
10956 int textarea_lineno = 0;
10957 unsigned form_is_special = 0;
10958
10959 CTRACE((tfp, "SubmitForm\n link_name=%s\n link_value=%s\n", link_name, link_value));
10960 if (!HTMainText)
10961 return 0;
10962
10963 thisform = HText_PerFormInfo(form_number);
10964 /* Sanity check */
10965 if (!thisform) {
10966 CTRACE((tfp, "SubmitForm: form %d not in HTMainText's list!\n",
10967 form_number));
10968 } else if (thisform->number != form_number) {
10969 CTRACE((tfp, "SubmitForm: failed sanity check, %d!=%d !\n",
10970 thisform->number, form_number));
10971 thisform = NULL;
10972 }
10973
10974 if (isEmpty(submit_item->submit_action)) {
10975 CTRACE((tfp, "SubmitForm: no action given\n"));
10976 return 0;
10977 }
10978
10979 /*
10980 * If we're mailing, make sure it's a mailto ACTION. -FM
10981 */
10982 if ((submit_item->submit_method == URL_MAIL_METHOD) &&
10983 !isMAILTO_URL(submit_item->submit_action)) {
10984 HTAlert(BAD_FORM_MAILTO);
10985 return 0;
10986 }
10987
10988 /*
10989 * Check the ENCTYPE and set up the appropriate variables. -FM
10990 */
10991 if (submit_item->submit_enctype &&
10992 !strncasecomp(submit_item->submit_enctype, STR_PLAINTEXT, 10)) {
10993 /*
10994 * Do not hex escape, and use physical newlines
10995 * to separate name=value pairs. -FM
10996 */
10997 PlainText = TRUE;
10998 } else if (submit_item->submit_enctype &&
10999 !strncasecomp(submit_item->submit_enctype,
11000 "application/sgml-form-urlencoded", 32)) {
11001 /*
11002 * Use semicolons instead of ampersands as the
11003 * separators for name=value pairs. -FM
11004 */
11005 SemiColon = TRUE;
11006 } else if (submit_item->submit_enctype &&
11007 !strncasecomp(submit_item->submit_enctype,
11008 "multipart/form-data", 19)) {
11009 /*
11010 * Use the multipart MIME format. Later we will ensure it does not
11011 * occur within the content.
11012 */
11013 StrAllocCopy(Boundary, "xnyLAaB03X");
11014 }
11015
11016 /*
11017 * Determine in what character encoding (aka. charset) we should
11018 * submit. We call this target_cs and the MIME name for it
11019 * target_csname.
11020 * TODO: - actually use ACCEPT-CHARSET stuff from FORM
11021 * TODO: - deal with list in ACCEPT-CHARSET, find a "best"
11022 * charset to submit
11023 */
11024
11025 /* Look at ACCEPT-CHARSET on the submitting field if we have one. */
11026 if (thisform && submit_item->accept_cs &&
11027 strcasecomp(submit_item->accept_cs, "UNKNOWN")) {
11028 have_accept_cs = YES;
11029 target_cs = find_best_target_cs(&thisform->thisacceptcs,
11030 current_char_set,
11031 submit_item->accept_cs);
11032 }
11033 /* Look at ACCEPT-CHARSET on form as a whole if submitting field
11034 * didn't have one. */
11035 if (thisform && !have_accept_cs && thisform->accept_cs &&
11036 strcasecomp(thisform->accept_cs, "UNKNOWN")) {
11037 have_accept_cs = YES;
11038 target_cs = find_best_target_cs(&thisform->thisacceptcs,
11039 current_char_set,
11040 thisform->accept_cs);
11041 }
11042 if (have_accept_cs && (target_cs >= 0) && thisform->thisacceptcs) {
11043 target_csname = thisform->thisacceptcs;
11044 }
11045
11046 if (target_cs < 0 &&
11047 non_empty(HTMainText->node_anchor->charset)) {
11048 target_cs = UCGetLYhndl_byMIME(HTMainText->node_anchor->charset);
11049 if (target_cs >= 0) {
11050 target_csname = HTMainText->node_anchor->charset;
11051 } else {
11052 target_cs = UCLYhndl_for_unspec; /* always >= 0 */
11053 target_csname = LYCharSet_UC[target_cs].MIMEname;
11054 }
11055 }
11056 if (target_cs < 0) {
11057 target_cs = UCLYhndl_for_unspec; /* always >= 0 */
11058 }
11059
11060 /*
11061 * Go through list of anchors and get a "max." charset parameter - kw
11062 */
11063 for (anchor_ptr = HTMainText->first_anchor;
11064 anchor_ptr != NULL;
11065 anchor_ptr = anchor_ptr->next) {
11066
11067 if (anchor_ptr->link_type != INPUT_ANCHOR)
11068 continue;
11069
11070 if (anchor_ptr->input_field->number == form_number &&
11071 !anchor_ptr->input_field->disabled) {
11072
11073 FormInfo *form_ptr = anchor_ptr->input_field;
11074 char *val = (form_ptr->cp_submit_value != NULL
11075 ? form_ptr->cp_submit_value
11076 : form_ptr->value);
11077
11078 unsigned field_is_special = check_form_specialchars(val);
11079 unsigned name_is_special = check_form_specialchars(form_ptr->name);
11080
11081 form_is_special = (field_is_special | name_is_special);
11082
11083 if (field_is_special == 0) {
11084 /* already ok */
11085 } else if (target_cs < 0) {
11086 /* already confused */
11087 } else if ((field_is_special & SPECIAL_8BIT) == 0
11088 && (LYCharSet_UC[target_cs].enc == UCT_ENC_8859
11089 || (LYCharSet_UC[target_cs].like8859 & UCT_R_8859SPECL))) {
11090 /* those specials will be trivial */
11091 } else if (UCNeedNotTranslate(form_ptr->value_cs, target_cs)) {
11092 /* already ok */
11093 } else if (UCCanTranslateFromTo(form_ptr->value_cs, target_cs)) {
11094 /* also ok */
11095 } else if (UCCanTranslateFromTo(target_cs, form_ptr->value_cs)) {
11096 target_cs = form_ptr->value_cs; /* try this */
11097 target_csname = NULL; /* will be set after loop */
11098 } else {
11099 target_cs = -1; /* don't know what to do */
11100 }
11101
11102 /* Same for name */
11103 if (name_is_special == 0) {
11104 /* already ok */
11105 } else if (target_cs < 0) {
11106 /* already confused */
11107 } else if ((name_is_special & SPECIAL_8BIT) == 0
11108 && (LYCharSet_UC[target_cs].enc == UCT_ENC_8859
11109 || (LYCharSet_UC[target_cs].like8859 & UCT_R_8859SPECL))) {
11110 /* those specials will be trivial */
11111 } else if (UCNeedNotTranslate(form_ptr->name_cs, target_cs)) {
11112 /* already ok */
11113 } else if (UCCanTranslateFromTo(form_ptr->name_cs, target_cs)) {
11114 /* also ok */
11115 } else if (UCCanTranslateFromTo(target_cs, form_ptr->name_cs)) {
11116 target_cs = form_ptr->value_cs; /* try this */
11117 target_csname = NULL; /* will be set after loop */
11118 } else {
11119 target_cs = -1; /* don't know what to do */
11120 }
11121
11122 ++anchor_limit;
11123 } else if (anchor_ptr->input_field->number > form_number) {
11124 break;
11125 }
11126 }
11127
11128 /*
11129 * If we have input fields (we expect this), make an array of them so we
11130 * can organize the data.
11131 */
11132 if (anchor_limit != 0) {
11133 my_data = typecallocn(PostData, (size_t) anchor_limit);
11134 if (my_data == 0)
11135 outofmem(__FILE__, "HText_SubmitForm");
11136 }
11137
11138 if (target_csname == NULL) {
11139 if (target_cs >= 0) {
11140 if ((form_is_special & SPECIAL_8BIT) != 0) {
11141 target_csname = LYCharSet_UC[target_cs].MIMEname;
11142 } else if ((form_is_special & SPECIAL_FORM) != 0) {
11143 target_csname = LYCharSet_UC[target_cs].MIMEname;
11144 } else {
11145 target_csname = "us-ascii";
11146 }
11147 } else {
11148 target_csname = "us-ascii";
11149 target_cs = UCLYhndl_for_unspec; /* always >= 0 */
11150 }
11151 } else if (target_cs < 0) {
11152 target_cs = UCLYhndl_for_unspec; /* always >= 0 */
11153 }
11154
11155 if (submit_item->submit_method == URL_GET_METHOD && Boundary == NULL) {
11156 char *temp = NULL;
11157
11158 StrAllocCopy(temp, submit_item->submit_action);
11159 /*
11160 * Method is GET. Clip out any anchor in the current URL.
11161 */
11162 strtok(temp, "#");
11163 /*
11164 * Clip out any old query in the current URL.
11165 */
11166 strtok(temp, "?");
11167 /*
11168 * Add the lead question mark for the new URL.
11169 */
11170 StrAllocCat(temp, "?");
11171 BStrCat0(my_query, temp);
11172 free(temp);
11173 } else {
11174 /*
11175 * We are submitting POST content to a server,
11176 * so load content_type_out. This will be put in
11177 * the post_content_type element if all goes well. -FM, kw
11178 */
11179 if (SemiColon == TRUE) {
11180 StrAllocCopy(content_type_out,
11181 "application/sgml-form-urlencoded");
11182 } else if (PlainText == TRUE) {
11183 StrAllocCopy(content_type_out,
11184 STR_PLAINTEXT);
11185 } else if (Boundary != NULL) {
11186 StrAllocCopy(content_type_out,
11187 "multipart/form-data");
11188 } else {
11189 StrAllocCopy(content_type_out,
11190 "application/x-www-form-urlencoded");
11191 }
11192
11193 /*
11194 * If the ENCTYPE is not multipart/form-data, append the
11195 * charset we'll be sending to the post_content_type, IF
11196 * (1) there was an explicit accept-charset attribute, OR
11197 * (2) we have 8-bit or special chars, AND the document had
11198 * an explicit (recognized and accepted) charset parameter,
11199 * AND it or target_csname is different from iso-8859-1,
11200 * OR
11201 * (3) we have 8-bit or special chars, AND the document had
11202 * no explicit (recognized and accepted) charset parameter,
11203 * AND target_cs is different from the currently effective
11204 * assumed charset (which should have been set by the user
11205 * so that it reflects what the server is sending, if the
11206 * document is rendered correctly).
11207 * For multipart/form-data the equivalent will be done later,
11208 * separately for each form field. - kw
11209 */
11210 if (have_accept_cs
11211 || ((form_is_special & SPECIAL_8BIT) != 0
11212 || (form_is_special & SPECIAL_FORM) != 0)) {
11213 if (target_cs >= 0 && target_csname) {
11214 if (Boundary == NULL) {
11215 if ((HTMainText->node_anchor->charset &&
11216 (strcmp(HTMainText->node_anchor->charset,
11217 "iso-8859-1") ||
11218 strcmp(target_csname, "iso-8859-1"))) ||
11219 (!HTMainText->node_anchor->charset &&
11220 target_cs != UCLYhndl_for_unspec)) {
11221 HTSprintf(&content_type_out, "; charset=%s", target_csname);
11222 }
11223 }
11224 } else {
11225 cannot_transcode(&had_chartrans_warning, target_csname);
11226 }
11227 }
11228 }
11229
11230 out_csname = target_csname;
11231
11232 /*
11233 * Build up a list of the input fields and their associated values.
11234 */
11235 for (anchor_ptr = HTMainText->first_anchor;
11236 anchor_ptr != NULL;
11237 anchor_ptr = anchor_ptr->next) {
11238
11239 if (anchor_ptr->link_type != INPUT_ANCHOR)
11240 continue;
11241
11242 if (anchor_ptr->input_field->number == form_number &&
11243 !anchor_ptr->input_field->disabled) {
11244
11245 FormInfo *form_ptr = anchor_ptr->input_field;
11246 int out_cs;
11247 QuoteData quoting = (PlainText
11248 ? NO_QUOTE
11249 : (Boundary
11250 ? QUOTE_MULTI
11251 : QUOTE_SPECIAL));
11252
11253 assert(my_data != NULL);
11254
11255 if (form_ptr->type != F_TEXTAREA_TYPE)
11256 textarea_lineno = 0;
11257
11258 CTRACE((tfp, "SubmitForm[%d/%d]: ",
11259 anchor_count + 1, anchor_limit));
11260
11261 name_used = NonNull(form_ptr->name);
11262
11263 switch (form_ptr->type) {
11264 case F_RESET_TYPE:
11265 CTRACE((tfp, "reset\n"));
11266 break;
11267 #ifdef USE_FILE_UPLOAD
11268 case F_FILE_TYPE:
11269 val_used = NonNull(form_ptr->value);
11270 CTRACE((tfp, "I will submit \"%s\" (from %s)\n",
11271 val_used, name_used));
11272 break;
11273 #endif
11274 case F_SUBMIT_TYPE:
11275 case F_TEXT_SUBMIT_TYPE:
11276 case F_IMAGE_SUBMIT_TYPE:
11277 if (!(non_empty(form_ptr->name) &&
11278 !strcmp(form_ptr->name, link_name))) {
11279 CTRACE((tfp, "skipping submit field with "));
11280 CTRACE((tfp, "name \"%s\" for link_name \"%s\", %s.\n",
11281 form_ptr->name ? form_ptr->name : "???",
11282 link_name ? link_name : "???",
11283 non_empty(form_ptr->name) ?
11284 "not current link" : "no field name"));
11285 break;
11286 }
11287 if (!(form_ptr->type == F_TEXT_SUBMIT_TYPE ||
11288 (non_empty(form_ptr->value) &&
11289 !strcmp(form_ptr->value, link_value)))) {
11290 CTRACE((tfp, "skipping submit field with "));
11291 CTRACE((tfp, "name \"%s\" for link_name \"%s\", %s!\n",
11292 form_ptr->name ? form_ptr->name : "???",
11293 link_name ? link_name : "???",
11294 "values are different"));
11295 break;
11296 }
11297 /* FALLTHRU */
11298 case F_RADIO_TYPE:
11299 case F_CHECKBOX_TYPE:
11300 case F_TEXTAREA_TYPE:
11301 case F_PASSWORD_TYPE:
11302 case F_TEXT_TYPE:
11303 case F_OPTION_LIST_TYPE:
11304 case F_HIDDEN_TYPE:
11305 /*
11306 * Be sure to actually look at the option submit value.
11307 */
11308 if (form_ptr->cp_submit_value != NULL) {
11309 val_used = form_ptr->cp_submit_value;
11310 } else {
11311 val_used = form_ptr->value;
11312 }
11313
11314 /*
11315 * Charset-translate value now, because we need to know the
11316 * charset parameter for multipart bodyparts. - kw
11317 */
11318 if (check_form_specialchars(val_used) != 0) {
11319 /* We should translate back. */
11320 StrAllocCopy(copied_val_used, val_used);
11321 success = FALSE;
11322 if (HTCJK == JAPANESE) {
11323 if ((0 <= target_cs) &&
11324 !strcmp(LYCharSet_UC[target_cs].MIMEname, "euc-jp")) {
11325 TO_EUC((const unsigned char *) val_used,
11326 (unsigned char *) copied_val_used);
11327 success = YES;
11328 } else if ((0 <= target_cs) &&
11329 !strcmp(LYCharSet_UC[target_cs].MIMEname,
11330 "shift_jis")) {
11331 TO_SJIS((const unsigned char *) val_used,
11332 (unsigned char *) copied_val_used);
11333 success = YES;
11334 }
11335 }
11336 if (!success) {
11337 success = LYUCTranslateBackFormData(&copied_val_used,
11338 form_ptr->value_cs,
11339 target_cs, PlainText);
11340 }
11341 CTRACE((tfp, "field \"%s\" %d %s -> %d %s %s\n",
11342 NonNull(form_ptr->name),
11343 form_ptr->value_cs,
11344 ((form_ptr->value_cs >= 0)
11345 ? LYCharSet_UC[form_ptr->value_cs].MIMEname
11346 : "???"),
11347 target_cs,
11348 target_csname ? target_csname : "???",
11349 success ? "OK" : "FAILED"));
11350 if (success) {
11351 val_used = copied_val_used;
11352 }
11353 } else { /* We can use the value directly. */
11354 CTRACE((tfp, "field \"%s\" %d %s OK\n",
11355 NonNull(form_ptr->name),
11356 target_cs,
11357 target_csname ? target_csname : "???"));
11358 success = YES;
11359 }
11360 if (!success) {
11361 cannot_transcode(&had_chartrans_warning, target_csname);
11362 out_cs = form_ptr->value_cs;
11363 } else {
11364 out_cs = target_cs;
11365 }
11366 if (out_cs >= 0)
11367 out_csname = LYCharSet_UC[out_cs].MIMEname;
11368 if (Boundary) {
11369 StrAllocCopy(MultipartContentType,
11370 "\r\nContent-Type: %s");
11371 if (!success && form_ptr->value_cs < 0) {
11372 /* This is weird. */
11373 out_csname = "UNKNOWN-8BIT";
11374 } else if (!success) {
11375 target_csname = NULL;
11376 } else {
11377 if (!target_csname) {
11378 target_csname = LYCharSet_UC[target_cs].MIMEname;
11379 }
11380 }
11381 if (strcmp(out_csname, "iso-8859-1"))
11382 HTSprintf(&MultipartContentType, "; charset=%s", out_csname);
11383 }
11384
11385 /*
11386 * Charset-translate name now, because we need to know the
11387 * charset parameter for multipart bodyparts. - kw
11388 */
11389 if (form_ptr->type == F_TEXTAREA_TYPE) {
11390 textarea_lineno++;
11391 if (textarea_lineno > 1 &&
11392 last_textarea_name && form_ptr->name &&
11393 !strcmp(last_textarea_name, form_ptr->name)) {
11394 break;
11395 }
11396 }
11397
11398 if (check_form_specialchars(name_used) != 0) {
11399 /* We should translate back. */
11400 StrAllocCopy(copied_name_used, name_used);
11401 success = LYUCTranslateBackFormData(&copied_name_used,
11402 form_ptr->name_cs,
11403 target_cs, PlainText);
11404 CTRACE((tfp, "name \"%s\" %d %s -> %d %s %s\n",
11405 NonNull(form_ptr->name),
11406 form_ptr->name_cs,
11407 ((form_ptr->name_cs >= 0)
11408 ? LYCharSet_UC[form_ptr->name_cs].MIMEname
11409 : "???"),
11410 target_cs,
11411 target_csname ? target_csname : "???",
11412 success ? "OK" : "FAILED"));
11413 if (success) {
11414 name_used = copied_name_used;
11415 }
11416 if (Boundary) {
11417 if (!success) {
11418 StrAllocCopy(MultipartContentType, "");
11419 target_csname = NULL;
11420 } else {
11421 if (!target_csname)
11422 target_csname = LYCharSet_UC[target_cs].MIMEname;
11423 }
11424 }
11425 } else { /* We can use the name directly. */
11426 CTRACE((tfp, "name \"%s\" %d %s OK\n",
11427 NonNull(form_ptr->name),
11428 target_cs,
11429 target_csname ? target_csname : "???"));
11430 success = YES;
11431 if (Boundary) {
11432 StrAllocCopy(copied_name_used, name_used);
11433 }
11434 }
11435 if (!success) {
11436 cannot_transcode(&had_chartrans_warning, target_csname);
11437 }
11438 if (Boundary) {
11439 /*
11440 * According to RFC 1867, Non-ASCII field names
11441 * "should be encoded according to the prescriptions
11442 * of RFC 1522 [...]. I don't think RFC 1522 actually
11443 * is meant to apply to parameters like this, and it
11444 * is unknown whether any server would make sense of
11445 * it, so for now just use some quoting/escaping and
11446 * otherwise leave 8-bit values as they are.
11447 * Non-ASCII characters in form field names submitted
11448 * as multipart/form-data can only occur if the form
11449 * provider specifically asked for it anyway. - kw
11450 */
11451 HTMake822Word(&copied_name_used, FALSE);
11452 name_used = copied_name_used;
11453 }
11454
11455 break;
11456 default:
11457 CTRACE((tfp, "What type is %d?\n", form_ptr->type));
11458 break;
11459 }
11460
11461 skip_field = FALSE;
11462 my_data[anchor_count].first = TRUE;
11463 my_data[anchor_count].type = form_ptr->type;
11464
11465 /*
11466 * Using the values of 'name_used' and 'val_used' computed in the
11467 * previous case-statement, compute the 'first' and 'data' values
11468 * for the current input field.
11469 */
11470 switch (form_ptr->type) {
11471
11472 default:
11473 skip_field = TRUE;
11474 break;
11475
11476 #ifdef USE_FILE_UPLOAD
11477 case F_FILE_TYPE:
11478 load_a_file(val_used, &(my_data[anchor_count].data));
11479 break;
11480 #endif /* USE_FILE_UPLOAD */
11481
11482 case F_SUBMIT_TYPE:
11483 case F_TEXT_SUBMIT_TYPE:
11484 case F_IMAGE_SUBMIT_TYPE:
11485 if ((non_empty(form_ptr->name) &&
11486 !strcmp(form_ptr->name, link_name)) &&
11487 (form_ptr->type == F_TEXT_SUBMIT_TYPE ||
11488 (non_empty(form_ptr->value) &&
11489 !strcmp(form_ptr->value, link_value)))) {
11490 ;
11491 } else {
11492 skip_field = TRUE;
11493 }
11494 break;
11495
11496 case F_RADIO_TYPE:
11497 case F_CHECKBOX_TYPE:
11498 /*
11499 * Only add if selected.
11500 */
11501 if (form_ptr->num_value) {
11502 ;
11503 } else {
11504 skip_field = TRUE;
11505 }
11506 break;
11507
11508 case F_TEXTAREA_TYPE:
11509 if (!last_textarea_name ||
11510 strcmp(last_textarea_name, form_ptr->name)) {
11511 textarea_lineno = 1;
11512 last_textarea_name = form_ptr->name;
11513 } else {
11514 my_data[anchor_count].first = FALSE;
11515 }
11516 break;
11517
11518 case F_PASSWORD_TYPE:
11519 case F_TEXT_TYPE:
11520 case F_OPTION_LIST_TYPE:
11521 case F_HIDDEN_TYPE:
11522 break;
11523 }
11524
11525 /*
11526 * If we did not decide to skip the current field, populate the
11527 * values in the array for it.
11528 */
11529 if (!skip_field) {
11530 StrAllocCopy(my_data[anchor_count].name, name_used);
11531 StrAllocCopy(my_data[anchor_count].value, val_used);
11532 if (my_data[anchor_count].data == 0)
11533 BStrCat0(my_data[anchor_count].data, val_used);
11534 my_data[anchor_count].quote = quoting;
11535 if (quoting == QUOTE_MULTI
11536 && check_if_base64_needed(submit_item->submit_method,
11537 my_data[anchor_count].data)) {
11538 CTRACE((tfp, "will encode as base64\n"));
11539 my_data[anchor_count].quote = QUOTE_BASE64;
11540 escaped2 =
11541 convert_to_base64(BStrData(my_data[anchor_count].data),
11542 (size_t)
11543 BStrLen(my_data[anchor_count].data));
11544 BStrCopy0(my_data[anchor_count].data, escaped2);
11545 FREE(escaped2);
11546 }
11547 }
11548 ++anchor_count;
11549
11550 FREE(copied_name_used);
11551 FREE(copied_val_used);
11552
11553 } else if (anchor_ptr->input_field->number > form_number) {
11554 break;
11555 }
11556 }
11557
11558 FREE(copied_name_used);
11559
11560 if (my_data != 0) {
11561 BOOL first_one = TRUE;
11562
11563 /*
11564 * If we're using a MIME-boundary, make it unique.
11565 */
11566 if (content_type_out != 0 && Boundary != 0) {
11567 Boundary = 0;
11568 StrAllocCopy(Boundary, "LYNX");
11569 for (anchor_count = 0; anchor_count < anchor_limit; ++anchor_count) {
11570 if (my_data[anchor_count].data != 0) {
11571 UpdateBoundary(&Boundary, my_data[anchor_count].data);
11572 }
11573 }
11574 HTSprintf(&content_type_out, "; boundary=%s", Boundary);
11575 }
11576
11577 for (anchor_count = 0; anchor_count < anchor_limit; ++anchor_count) {
11578
11579 if (my_data[anchor_count].name != 0
11580 && my_data[anchor_count].value != 0) {
11581
11582 CTRACE((tfp,
11583 "processing [%d:%d] name=%s(first:%d, value=%s, data=%p)\n",
11584 anchor_count + 1,
11585 anchor_limit,
11586 NonNull(my_data[anchor_count].name),
11587 my_data[anchor_count].first,
11588 NonNull(my_data[anchor_count].value),
11589 (void *) my_data[anchor_count].data));
11590
11591 if (my_data[anchor_count].first) {
11592 if (first_one) {
11593 if (Boundary) {
11594 HTBprintf(&my_query, "--%s\r\n", Boundary);
11595 }
11596 first_one = FALSE;
11597 } else {
11598 if (PlainText) {
11599 BStrCat0(my_query, "\n");
11600 } else if (SemiColon) {
11601 BStrCat0(my_query, ";");
11602 } else if (Boundary) {
11603 HTBprintf(&my_query, "\r\n--%s\r\n", Boundary);
11604 } else {
11605 BStrCat0(my_query, "&");
11606 }
11607 }
11608 }
11609
11610 /* append a null to the string */
11611 HTSABCat(&(my_data[anchor_count].data), "", 1);
11612 name_used = my_data[anchor_count].name;
11613 val_used = my_data[anchor_count].value;
11614
11615 } else {
11616 /* there is no data to send */
11617 continue;
11618 }
11619
11620 switch (my_data[anchor_count].type) {
11621 case F_TEXT_TYPE:
11622 case F_PASSWORD_TYPE:
11623 case F_OPTION_LIST_TYPE:
11624 case F_HIDDEN_TYPE:
11625 escaped1 = escape_or_quote_name(my_data[anchor_count].name,
11626 my_data[anchor_count].quote,
11627 MultipartContentType);
11628
11629 escaped2 = escape_or_quote_value(val_used,
11630 my_data[anchor_count].quote);
11631
11632 HTBprintf(&my_query,
11633 "%s%s%s%s%s",
11634 escaped1,
11635 (Boundary ? "" : "="),
11636 (PlainText ? "\n" : ""),
11637 escaped2,
11638 ((PlainText && *escaped2) ? "\n" : ""));
11639 break;
11640 case F_CHECKBOX_TYPE:
11641 case F_RADIO_TYPE:
11642 escaped1 = escape_or_quote_name(my_data[anchor_count].name,
11643 my_data[anchor_count].quote,
11644 MultipartContentType);
11645
11646 escaped2 = escape_or_quote_value(val_used,
11647 my_data[anchor_count].quote);
11648
11649 HTBprintf(&my_query,
11650 "%s%s%s%s%s",
11651 escaped1,
11652 (Boundary ? "" : "="),
11653 (PlainText ? "\n" : ""),
11654 escaped2,
11655 ((PlainText && *escaped2) ? "\n" : ""));
11656 break;
11657 case F_SUBMIT_TYPE:
11658 case F_TEXT_SUBMIT_TYPE:
11659 case F_IMAGE_SUBMIT_TYPE:
11660 /*
11661 * If it has a non-zero length name (e.g., because
11662 * its IMAGE_SUBMIT_TYPE is to be handled homologously
11663 * to an image map, or a SUBMIT_TYPE in a set of
11664 * multiple submit buttons, or a single type="text"
11665 * that's been converted to a TEXT_SUBMIT_TYPE),
11666 * include the name=value pair, or fake name.x=0 and
11667 * name.y=0 pairs for IMAGE_SUBMIT_TYPE. -FM
11668 */
11669 escaped1 = escape_or_quote_name(my_data[anchor_count].name,
11670 my_data[anchor_count].quote,
11671 MultipartContentType);
11672
11673 escaped2 = escape_or_quote_value(val_used,
11674 my_data[anchor_count].quote);
11675
11676 if (my_data[anchor_count].type == F_IMAGE_SUBMIT_TYPE) {
11677 /*
11678 * It's a clickable image submit button. Fake a 0,0
11679 * coordinate pair, which typically returns the image's
11680 * default. -FM
11681 */
11682 if (Boundary) {
11683 *(StrChr(escaped1, '=') + 1) = '\0';
11684 HTBprintf(&my_query,
11685 "%s\"%s.x\"\r\n\r\n0\r\n--%s\r\n%s\"%s.y\"\r\n\r\n0",
11686 escaped1,
11687 my_data[anchor_count].name,
11688 Boundary,
11689 escaped1,
11690 my_data[anchor_count].name);
11691 } else {
11692 HTBprintf(&my_query,
11693 "%s.x=0%s%s.y=0%s",
11694 escaped1,
11695 (PlainText ?
11696 "\n" : (SemiColon ?
11697 ";" : "&")),
11698 escaped1,
11699 ((PlainText && *escaped1) ?
11700 "\n" : ""));
11701 }
11702 } else {
11703 /*
11704 * It's a standard submit button. Use the name=value
11705 * pair. = FM
11706 */
11707 HTBprintf(&my_query,
11708 "%s%s%s%s%s",
11709 escaped1,
11710 (Boundary ? "" : "="),
11711 (PlainText ? "\n" : ""),
11712 escaped2,
11713 ((PlainText && *escaped2) ? "\n" : ""));
11714 }
11715 break;
11716 case F_RESET_TYPE:
11717 /* ignore */
11718 break;
11719 case F_TEXTAREA_TYPE:
11720 escaped2 = escape_or_quote_value(val_used,
11721 my_data[anchor_count].quote);
11722
11723 if (my_data[anchor_count].first) {
11724 /*
11725 * Names are different so this is the first textarea or a
11726 * different one from any before it.
11727 */
11728 if (PlainText) {
11729 FREE(previous_blanks);
11730 } else if (Boundary) {
11731 StrAllocCopy(previous_blanks, "\r\n");
11732 } else {
11733 StrAllocCopy(previous_blanks, "%0d%0a");
11734 }
11735 escaped1 = escape_or_quote_name(name_used,
11736 my_data[anchor_count].quote,
11737 MultipartContentType);
11738
11739 HTBprintf(&my_query,
11740 "%s%s%s%s%s",
11741 escaped1,
11742 (Boundary ? "" : "="),
11743 (PlainText ? "\n" : ""),
11744 escaped2,
11745 ((PlainText && *escaped2) ? "\n" : ""));
11746 } else {
11747 const char *marker = (PlainText
11748 ? "\n"
11749 : (Boundary
11750 ? "\r\n"
11751 : "%0d%0a"));
11752
11753 /*
11754 * This is a continuation of a previous textarea.
11755 */
11756 if (escaped2[0] != '\0') {
11757 if (previous_blanks) {
11758 BStrCat0(my_query, previous_blanks);
11759 FREE(previous_blanks);
11760 }
11761 BStrCat0(my_query, escaped2);
11762 if (PlainText || Boundary)
11763 BStrCat0(my_query, marker);
11764 else
11765 StrAllocCopy(previous_blanks, marker);
11766 } else {
11767 StrAllocCat(previous_blanks, marker);
11768 }
11769 }
11770 break;
11771 case F_RANGE_TYPE:
11772 /* not implemented */
11773 break;
11774 #ifdef USE_FILE_UPLOAD
11775 case F_FILE_TYPE:
11776 if (PlainText) {
11777 StrAllocCopy(escaped1, my_data[anchor_count].name);
11778 } else if (Boundary) {
11779 const char *t = guess_content_type(val_used);
11780 char *copied_fname = NULL;
11781
11782 StrAllocCopy(escaped1, "Content-Disposition: form-data");
11783 HTSprintf(&escaped1, "; name=\"%s\"",
11784 my_data[anchor_count].name);
11785
11786 StrAllocCopy(copied_fname, val_used);
11787 HTMake822Word(&copied_fname, FALSE);
11788 HTSprintf(&escaped1, "; filename=\"%s\"", copied_fname);
11789 FREE(copied_fname);
11790
11791 /* Should we take into account the encoding? */
11792 HTSprintf(&escaped1, "\r\nContent-Type: %s", t);
11793 if (my_data[anchor_count].quote == QUOTE_BASE64)
11794 StrAllocCat(escaped1,
11795 "\r\nContent-Transfer-Encoding: base64");
11796 StrAllocCat(escaped1, "\r\n\r\n");
11797 } else {
11798 escaped1 = HTEscapeSP(my_data[anchor_count].name, URL_XALPHAS);
11799 }
11800
11801 HTBprintf(&my_query,
11802 "%s%s%s",
11803 escaped1,
11804 (Boundary ? "" : "="),
11805 (PlainText ? "\n" : ""));
11806 /*
11807 * If we have anything more than the trailing null we added,
11808 * append the file-data to the query.
11809 */
11810 if (BStrLen(my_data[anchor_count].data) > 1) {
11811 HTSABCat(&my_query,
11812 BStrData(my_data[anchor_count].data),
11813 BStrLen(my_data[anchor_count].data) - 1);
11814 if (PlainText)
11815 HTBprintf(&my_query, "\n");
11816 }
11817 break;
11818 #endif /* USE_FILE_UPLOAD */
11819 case F_KEYGEN_TYPE:
11820 case F_BUTTON_TYPE:
11821 /* not implemented */
11822 break;
11823 }
11824
11825 FREE(escaped1);
11826 FREE(escaped2);
11827 }
11828 if (Boundary) {
11829 HTBprintf(&my_query, "\r\n--%s--\r\n", Boundary);
11830 }
11831 if (TRACE) {
11832 CTRACE((tfp, "Query %d{", BStrLen(my_query)));
11833 trace_bstring(my_query);
11834 CTRACE((tfp, "}\n"));
11835 }
11836 }
11837
11838 if (submit_item->submit_method == URL_MAIL_METHOD) {
11839 HTUserMsg2(gettext("Submitting %s"), submit_item->submit_action);
11840 HTSABCat(&my_query, "", 1); /* append null */
11841 mailform((submit_item->submit_action + 7),
11842 (isEmpty(submit_item->submit_title)
11843 ? NonNull(HText_getTitle())
11844 : submit_item->submit_title),
11845 BStrData(my_query),
11846 content_type_out);
11847 result = 0;
11848 BStrFree(my_query);
11849 FREE(content_type_out);
11850 } else {
11851 _statusline(SUBMITTING_FORM);
11852
11853 /*
11854 * File-URLs (whether via GET or POST) cannot provide search queries.
11855 * The relevant RFCs 1630, 1738 are silent on what to do with
11856 * unexpected query parameters in a file-URL.
11857 *
11858 * Internet Explorer trims the query string here (after all, a "?" is
11859 * not a legal part of a Windows filename), and other browsers copy the
11860 * behavior. We do this for compatibility, in case someone cares.
11861 */
11862 if (my_query != 0 &&
11863 my_query->len > 5 &&
11864 !strncmp(my_query->str, "file:", (size_t) 5)) {
11865 strtok(my_query->str, "?");
11866 }
11867 if (submit_item->submit_method == URL_POST_METHOD || Boundary) {
11868 LYFreePostData(doc);
11869 doc->post_data = my_query;
11870 doc->post_content_type = content_type_out; /* don't free c_t_out */
11871 CTRACE((tfp, "GridText - post_data set:\n%s\n", content_type_out));
11872 StrAllocCopy(doc->address, submit_item->submit_action);
11873 } else { /* GET_METHOD */
11874 HTSABCat(&my_query, "", 1); /* append null */
11875 StrAllocCopy(doc->address, BStrData(my_query));
11876 LYFreePostData(doc);
11877 FREE(content_type_out);
11878 HTSABFree(&my_query);
11879 }
11880 result = 1;
11881 }
11882
11883 FREE(MultipartContentType);
11884 FREE(previous_blanks);
11885 FREE(Boundary);
11886 if (my_data != 0) {
11887 for (anchor_count = 0; anchor_count < anchor_limit; ++anchor_count) {
11888 FREE(my_data[anchor_count].name);
11889 FREE(my_data[anchor_count].value);
11890 BStrFree(my_data[anchor_count].data);
11891 }
11892 FREE(my_data);
11893 }
11894
11895 return (result);
11896 }
11897
HText_DisableCurrentForm(void)11898 void HText_DisableCurrentForm(void)
11899 {
11900 TextAnchor *anchor_ptr;
11901
11902 HTFormDisabled = TRUE;
11903 if (HTMainText != NULL) {
11904 /*
11905 * Go through list of anchors and set the disabled flag.
11906 */
11907 for (anchor_ptr = HTMainText->first_anchor;
11908 anchor_ptr != NULL;
11909 anchor_ptr = anchor_ptr->next) {
11910
11911 if (anchor_ptr->link_type == INPUT_ANCHOR &&
11912 anchor_ptr->input_field->number == HTFormNumber) {
11913
11914 anchor_ptr->input_field->disabled = TRUE;
11915 }
11916 }
11917 }
11918 return;
11919 }
11920
HText_ResetForm(FormInfo * form)11921 void HText_ResetForm(FormInfo * form)
11922 {
11923 TextAnchor *anchor_ptr;
11924
11925 _statusline(RESETTING_FORM);
11926 if (HTMainText == 0)
11927 return;
11928
11929 /*
11930 * Go through list of anchors and reset values.
11931 */
11932 for (anchor_ptr = HTMainText->first_anchor;
11933 anchor_ptr != NULL;
11934 anchor_ptr = anchor_ptr->next) {
11935 if (anchor_ptr->link_type == INPUT_ANCHOR) {
11936 if (anchor_ptr->input_field->number == form->number) {
11937
11938 if (anchor_ptr->input_field->type == F_RADIO_TYPE ||
11939 anchor_ptr->input_field->type == F_CHECKBOX_TYPE) {
11940
11941 if (anchor_ptr->input_field->orig_value[0] == '0')
11942 anchor_ptr->input_field->num_value = 0;
11943 else
11944 anchor_ptr->input_field->num_value = 1;
11945
11946 } else if (anchor_ptr->input_field->type ==
11947 F_OPTION_LIST_TYPE) {
11948 anchor_ptr->input_field->value =
11949 anchor_ptr->input_field->orig_value;
11950
11951 anchor_ptr->input_field->cp_submit_value =
11952 anchor_ptr->input_field->orig_submit_value;
11953
11954 } else {
11955 StrAllocCopy(anchor_ptr->input_field->value,
11956 anchor_ptr->input_field->orig_value);
11957 }
11958 } else if (anchor_ptr->input_field->number > form->number) {
11959 break;
11960 }
11961 }
11962 }
11963 }
11964
11965 /*
11966 * This function is called before reloading/reparsing current document to find
11967 * whether any forms content was changed by user so any information will be
11968 * lost.
11969 */
HText_HaveUserChangedForms(HText * text)11970 BOOLEAN HText_HaveUserChangedForms(HText *text)
11971 {
11972 TextAnchor *anchor_ptr;
11973
11974 if (text == 0)
11975 return FALSE;
11976
11977 /*
11978 * Go through list of anchors to check if any value was changed.
11979 * This code based on HText_ResetForm()
11980 */
11981 for (anchor_ptr = text->first_anchor;
11982 anchor_ptr != NULL;
11983 anchor_ptr = anchor_ptr->next) {
11984 if (anchor_ptr->link_type == INPUT_ANCHOR) {
11985
11986 if (anchor_ptr->input_field->type == F_RADIO_TYPE ||
11987 anchor_ptr->input_field->type == F_CHECKBOX_TYPE) {
11988
11989 if ((anchor_ptr->input_field->orig_value[0] == '0' &&
11990 anchor_ptr->input_field->num_value == 1) ||
11991 (anchor_ptr->input_field->orig_value[0] != '0' &&
11992 anchor_ptr->input_field->num_value == 0))
11993 return TRUE;
11994
11995 } else if (anchor_ptr->input_field->type == F_OPTION_LIST_TYPE) {
11996 if (strcmp(anchor_ptr->input_field->value,
11997 anchor_ptr->input_field->orig_value))
11998 return TRUE;
11999
12000 if (strcmp(anchor_ptr->input_field->cp_submit_value,
12001 anchor_ptr->input_field->orig_submit_value))
12002 return TRUE;
12003
12004 } else {
12005 if (strcmp(anchor_ptr->input_field->value,
12006 anchor_ptr->input_field->orig_value))
12007 return TRUE;
12008 }
12009 }
12010 }
12011 return FALSE;
12012 }
12013
HText_activateRadioButton(FormInfo * form)12014 void HText_activateRadioButton(FormInfo * form)
12015 {
12016 TextAnchor *anchor_ptr;
12017 int form_number = form->number;
12018
12019 if (!HTMainText)
12020 return;
12021 for (anchor_ptr = HTMainText->first_anchor;
12022 anchor_ptr != NULL;
12023 anchor_ptr = anchor_ptr->next) {
12024 if (anchor_ptr->link_type == INPUT_ANCHOR &&
12025 anchor_ptr->input_field->type == F_RADIO_TYPE) {
12026
12027 if (anchor_ptr->input_field->number == form_number) {
12028
12029 /* if it has the same name and its on */
12030 if (!strcmp(anchor_ptr->input_field->name, form->name) &&
12031 anchor_ptr->input_field->num_value) {
12032 anchor_ptr->input_field->num_value = 0;
12033 break;
12034 }
12035 } else if (anchor_ptr->input_field->number > form_number) {
12036 break;
12037 }
12038
12039 }
12040 }
12041
12042 form->num_value = 1;
12043 }
12044
12045 #ifdef LY_FIND_LEAKS
12046 /*
12047 * Purpose: Free all currently loaded HText objects in memory.
12048 * Arguments: void
12049 * Return Value: void
12050 * Remarks/Portability/Dependencies/Restrictions:
12051 * Usage of this function should really be limited to program
12052 * termination.
12053 * Revision History:
12054 * 05-27-94 created Lynx 2-3-1 Garrett Arch Blythe
12055 */
free_all_texts(void)12056 static void free_all_texts(void)
12057 {
12058 HText *cur = NULL;
12059
12060 if (!loaded_texts)
12061 return;
12062
12063 /*
12064 * Simply loop through the loaded texts list killing them off.
12065 */
12066 while (loaded_texts && !HTList_isEmpty(loaded_texts)) {
12067 if ((cur = (HText *) HTList_removeLastObject(loaded_texts)) != NULL) {
12068 HText_free(cur);
12069 }
12070 }
12071
12072 /*
12073 * Get rid of the text list.
12074 */
12075 if (loaded_texts) {
12076 HTList_delete(loaded_texts);
12077 }
12078
12079 /*
12080 * Insurance for bad HTML.
12081 */
12082 FREE(HTCurSelectGroup);
12083 FREE(HTCurSelectGroupSize);
12084 FREE(HTCurSelectedOptionValue);
12085 PerFormInfo_free(HTCurrentForm);
12086
12087 return;
12088 }
12089 #endif /* LY_FIND_LEAKS */
12090
12091 /*
12092 * stub_HTAnchor_address is like HTAnchor_address, but it returns the
12093 * parent address for child links. This is only useful for traversal's
12094 * where one does not want to index a text file N times, once for each
12095 * of N internal links. Since the parent link has already been taken,
12096 * it won't go again, hence the (incorrect) links won't cause problems.
12097 */
stub_HTAnchor_address(HTAnchor * me)12098 char *stub_HTAnchor_address(HTAnchor * me)
12099 {
12100 char *addr = NULL;
12101
12102 if (me)
12103 StrAllocCopy(addr, me->parent->address);
12104 return addr;
12105 }
12106
HText_setToolbar(HText * text)12107 void HText_setToolbar(HText *text)
12108 {
12109 if (text)
12110 text->toolbar = TRUE;
12111 return;
12112 }
12113
HText_hasToolbar(HText * text)12114 BOOL HText_hasToolbar(HText *text)
12115 {
12116 return (BOOL) ((text && text->toolbar) ? TRUE : FALSE);
12117 }
12118
HText_setNoCache(HText * text)12119 void HText_setNoCache(HText *text)
12120 {
12121 if (text)
12122 text->no_cache = TRUE;
12123 return;
12124 }
12125
HText_hasNoCacheSet(HText * text)12126 BOOL HText_hasNoCacheSet(HText *text)
12127 {
12128 return (BOOL) ((text && text->no_cache) ? TRUE : FALSE);
12129 }
12130
HText_hasUTF8OutputSet(HText * text)12131 BOOL HText_hasUTF8OutputSet(HText *text)
12132 {
12133 return (BOOL) ((text && text->T.output_utf8) ? TRUE : FALSE);
12134 }
12135
12136 /*
12137 * Check charset and set the kcode element. -FM
12138 * Info on the input charset may be passed in in two forms,
12139 * as a string (if given explicitly) and as a pointer to
12140 * a LYUCcharset (from chartrans mechanism); either can be NULL.
12141 * For Japanese the kcode will be reset at a space or explicit
12142 * line or paragraph break, so what we set here may not last for
12143 * long. It's potentially more important not to set HTCJK to
12144 * NOCJK unless we are sure. - kw
12145 */
HText_setKcode(HText * text,const char * charset,LYUCcharset * p_in)12146 void HText_setKcode(HText *text, const char *charset,
12147 LYUCcharset *p_in)
12148 {
12149 BOOL charset_explicit;
12150
12151 if (!text)
12152 return;
12153
12154 /*
12155 * Check whether we have some kind of info. - kw
12156 */
12157 if (!charset && !p_in) {
12158 return;
12159 }
12160 charset_explicit = (BOOLEAN) (charset ? TRUE : FALSE);
12161 /*
12162 * If no explicit charset string, use the implied one. - kw
12163 */
12164 if (isEmpty(charset)) {
12165 charset = p_in->MIMEname;
12166 }
12167 /*
12168 * Check whether we have a specified charset. -FM
12169 */
12170 if (isEmpty(charset)) {
12171 return;
12172 }
12173
12174 /*
12175 * We've included the charset, and not forced a download offer,
12176 * only if the currently selected character set can handle it,
12177 * so check the charset value and set the text->kcode element
12178 * appropriately. -FM
12179 */
12180 /* If charset isn't specified explicitely nor assumed,
12181 * p_in->MIMEname would be set as display charset.
12182 * So text->kcode sholud be set as SJIS or EUC here only if charset
12183 * is specified explicitely, otherwise text->kcode would cause
12184 * mishandling Japanese strings. -- TH
12185 */
12186 if (charset_explicit && (!strcmp(charset, "shift_jis") ||
12187 !strcmp(charset, "x-sjis") || /* 1997/11/28 (Fri) 18:11:33 */
12188 !strcmp(charset, "x-shift-jis"))) {
12189 text->kcode = SJIS;
12190 } else if (charset_explicit
12191 #ifdef EXP_JAPANESEUTF8_SUPPORT
12192 && strcmp(charset, "utf-8")
12193 #endif
12194 && ((p_in && (p_in->enc == UCT_ENC_CJK)) ||
12195 !strcmp(charset, "x-euc") || /* 1997/11/28 (Fri) 18:11:24 */
12196 !strcmp(charset, "euc-jp") ||
12197 !StrNCmp(charset, "x-euc-", 6) ||
12198 !strcmp(charset, "euc-kr") ||
12199 !strcmp(charset, "iso-2022-kr") ||
12200 !strcmp(charset, "big5") ||
12201 !strcmp(charset, "cn-big5") ||
12202 !strcmp(charset, "euc-cn") ||
12203 !strcmp(charset, "gb2312") ||
12204 !StrNCmp(charset, "cn-gb", 5) ||
12205 !strcmp(charset, "iso-2022-cn"))) {
12206 text->kcode = EUC;
12207 } else {
12208 /*
12209 * If we get to here, it's not CJK, so disable that if
12210 * it is enabled. But only if we are quite sure. -FM & kw
12211 */
12212 text->kcode = NOKANJI;
12213 if (IS_CJK_TTY) {
12214 if (!p_in || ((p_in->enc != UCT_ENC_CJK)
12215 #ifdef EXP_JAPANESEUTF8_SUPPORT
12216 && (p_in->enc != UCT_ENC_UTF8)
12217 #endif
12218 )) {
12219 HTCJK = NOCJK;
12220 }
12221 }
12222 }
12223
12224 if (charset_explicit
12225 #ifdef EXP_JAPANESEUTF8_SUPPORT
12226 && strcmp(charset, "utf-8")
12227 #endif
12228 ) {
12229 text->specified_kcode = text->kcode;
12230 } else {
12231 if (UCAssume_MIMEcharset) {
12232 if (!strcmp(UCAssume_MIMEcharset, "euc-jp"))
12233 text->kcode = text->specified_kcode = EUC;
12234 else if (!strcmp(UCAssume_MIMEcharset, "shift_jis"))
12235 text->kcode = text->specified_kcode = SJIS;
12236 }
12237 }
12238
12239 return;
12240 }
12241
12242 /*
12243 * Set a permissible split at the current end of the last line. -FM
12244 */
HText_setBreakPoint(HText * text)12245 void HText_setBreakPoint(HText *text)
12246 {
12247 if (!text)
12248 return;
12249
12250 /*
12251 * Can split here. -FM
12252 */
12253 text->permissible_split = text->last_line->size;
12254
12255 return;
12256 }
12257
12258 /*
12259 * This function determines whether a document which
12260 * would be sought via the a URL that has a fragment
12261 * directive appended is otherwise identical to the
12262 * currently loaded document, and if so, returns
12263 * FALSE, so that any no_cache directives can be
12264 * overridden "safely", on the grounds that we are
12265 * simply acting on the equivalent of a paging
12266 * command. Otherwise, it returns TRUE, i.e, that
12267 * the target document might differ from the current,
12268 * based on any caching directives or analyses which
12269 * claimed or suggested this. -FM
12270 */
HText_AreDifferent(HTParentAnchor * anchor,const char * full_address)12271 BOOL HText_AreDifferent(HTParentAnchor *anchor,
12272 const char *full_address)
12273 {
12274 HTParentAnchor *MTanc;
12275 char *MTaddress;
12276 char *MTpound;
12277
12278 /*
12279 * Do we have a loaded document and both
12280 * arguments for this function?
12281 */
12282 if (!(HTMainText && anchor && full_address))
12283 return TRUE;
12284
12285 /*
12286 * Do we have both URLs?
12287 */
12288 MTanc = HTMainText->node_anchor;
12289 if (!(MTanc->address && anchor->address))
12290 return (TRUE);
12291
12292 /*
12293 * Do we have a fragment associated with the target?
12294 */
12295 if (findPoundSelector(full_address) == NULL)
12296 return (TRUE);
12297
12298 /*
12299 * Always treat client-side image map menus
12300 * as potentially stale, so we'll create a
12301 * fresh menu from the LynxMaps HTList.
12302 */
12303 if (isLYNXIMGMAP(anchor->address))
12304 return (TRUE);
12305
12306 /*
12307 * Do the docs differ in the type of request?
12308 */
12309 if (MTanc->isHEAD != anchor->isHEAD)
12310 return (TRUE);
12311
12312 /*
12313 * Are the actual URLs different, after factoring
12314 * out a "LYNXIMGMAP:" leader in the MainText URL
12315 * and its fragment, if present?
12316 */
12317 MTaddress = (isLYNXIMGMAP(MTanc->address)
12318 ? MTanc->address + LEN_LYNXIMGMAP
12319 : MTanc->address);
12320 MTpound = trimPoundSelector(MTaddress);
12321 if (strcmp(MTaddress, anchor->address)) {
12322 restorePoundSelector(MTpound);
12323 return (TRUE);
12324 }
12325 restorePoundSelector(MTpound);
12326
12327 /*
12328 * If the MainText is not an image map menu,
12329 * do the docs have different POST contents?
12330 */
12331 if (MTaddress == MTanc->address) {
12332 if (MTanc->post_data) {
12333 if (anchor->post_data) {
12334 if (!BINEQ(MTanc->post_data, anchor->post_data)) {
12335 /*
12336 * Both have contents, and they differ.
12337 */
12338 return (TRUE);
12339 }
12340 } else {
12341 /*
12342 * The loaded document has content, but the
12343 * target doesn't, so they're different.
12344 */
12345 return (TRUE);
12346 }
12347 } else if (anchor->post_data) {
12348 /*
12349 * The loaded document does not have content, but
12350 * the target does, so they're different.
12351 */
12352 return (TRUE);
12353 }
12354 }
12355
12356 /*
12357 * We'll assume the target is a position in the currently
12358 * displayed document, and thus can ignore any header, META,
12359 * or other directives not to use a cached rendition. -FM
12360 */
12361 return (FALSE);
12362 }
12363
12364 #define CanTrimTextArea(c) \
12365 (LYtrimInputFields ? isspace(c) : ((c) == '\r' || (c) == '\n'))
12366
12367 /*
12368 * Re-render the text of a tagged ("[123]") HTLine (arg1), with the tag
12369 * number incremented by some value (arg5). The re-rendered string may
12370 * be allowed to expand in the event of a tag width change (eg, 99 -> 100)
12371 * as controlled by arg6 (CHOP or NOCHOP). Arg4 is either (the address
12372 * of) a value which must match, in order for the tag to be incremented,
12373 * or (the address of) a 0-value, which will match any value, and cause
12374 * any valid tag to be incremented. Arg2 is a pointer to the first/only
12375 * anchor that exists on the line; we may need to adjust their position(s)
12376 * on the line. Arg3 when non-0 indicates the number of new digits that
12377 * were added to the 2nd line in a line crossing pair.
12378 *
12379 * All tags fields in a line which individually match an expected new value,
12380 * are incremented. Line crossing [tags] are handled (PITA).
12381 *
12382 * Untagged or improperly tagged lines are not altered.
12383 *
12384 * Returns the number of chars added to the original string's length, if
12385 * any.
12386 *
12387 * --KED 02/03/99
12388 */
increment_tagged_htline(HTLine * ht,TextAnchor * a,int * lx_val,int * old_val,int incr,int mode)12389 static int increment_tagged_htline(HTLine *ht, TextAnchor *a, int *lx_val,
12390 int *old_val,
12391 int incr,
12392 int mode)
12393 {
12394 char buf[MAX_LINE];
12395 char lxbuf[MAX_LINE * 2];
12396
12397 TextAnchor *st_anchor = a;
12398 TextAnchor *nxt_anchor;
12399
12400 char *p = ht->data;
12401 char *s = buf;
12402 char *lx = lxbuf;
12403 char *t;
12404
12405 BOOLEAN plx = FALSE;
12406 BOOLEAN valid;
12407
12408 int val;
12409 int n;
12410 int new_n;
12411 int pre_n;
12412 int post_n;
12413 int fixup = 0;
12414
12415 /*
12416 * Cleanup for the 2nd half of a line crosser, whose number of tag
12417 * digits grew by some number of places (usually 1 when it does
12418 * happen, though it *could* be more). The tag chars were already
12419 * rendered into the 2nd line of the pair, but the positioning and
12420 * other effects haven't been rippled through any other anchors on
12421 * the (2nd) line. So we do that here, as a special case, since
12422 * the part of the tag that's in the 2nd line of the pair, will not
12423 * be found by the tag string parsing code. Double PITA.
12424 *
12425 * [see comments below on line crosser caused problems]
12426 */
12427 if (*lx_val != 0) {
12428 nxt_anchor = st_anchor;
12429 while ((nxt_anchor) && (nxt_anchor->line_num == a->line_num)) {
12430 nxt_anchor->line_pos = (short) (nxt_anchor->line_pos + *lx_val);
12431 nxt_anchor = nxt_anchor->next;
12432 }
12433 fixup = *lx_val;
12434 *lx_val = 0;
12435 if (st_anchor)
12436 st_anchor = st_anchor->next;
12437 }
12438
12439 /*
12440 * Walk thru the line looking for tags (ie, "[nnn]" strings).
12441 */
12442 while (*p != '\0') {
12443 if (*p != '[') {
12444 *s++ = *p++;
12445 continue;
12446
12447 } else {
12448 *s++ = *p++;
12449 t = p;
12450 n = 0;
12451 valid = TRUE; /* p = t = byte after '[' */
12452
12453 /*
12454 * Make sure there are only digits between "[" and "]".
12455 */
12456 while (*t != ']') {
12457 if (*t == '\0') { /* uhoh - we have a potential line crosser */
12458 valid = FALSE;
12459 plx = TRUE;
12460 break;
12461 }
12462 if (isdigit(UCH(*t++))) {
12463 n++;
12464 continue;
12465 } else {
12466 valid = FALSE;
12467 break;
12468 }
12469 }
12470
12471 /*
12472 * If the format is OK, we check to see if the value is what
12473 * we expect. If not, we have a random [nn] string in the text,
12474 * and leave it alone.
12475 *
12476 * [It is *possible* to have a false match here, *if* there are
12477 * two identical [nn] strings (including the numeric value of
12478 * nn), one of which is the [tag], and the other being part of
12479 * a document. In such a case, the 1st [nn] string will get
12480 * incremented; the 2nd one won't, which makes it a 50-50 chance
12481 * of being correct, if and when such an unlikely juxtaposition
12482 * of text ever occurs. Further validation tests of the [nnn]
12483 * string are probably not possible, since little of the actual
12484 * anchor-associated-text is retained in the TextAnchor or the
12485 * FormInfo structs. Fortunately, I think the current method is
12486 * more than adequate to weed out 99.999% of any possible false
12487 * matches, just as it stands. Caveat emptor.]
12488 */
12489 if ((valid) && (n > 0)) {
12490 val = atoi(p);
12491 if ((val == *old_val) || (*old_val == 0)) { /* 0 matches all */
12492 if (*old_val != 0)
12493 (*old_val)++;
12494 val += incr;
12495 sprintf(s, "%d", val);
12496 new_n = (int) strlen(s);
12497 s += new_n;
12498 p += n;
12499
12500 /*
12501 * If the number of digits in an existing [tag] increased
12502 * (eg, [99] --> [100], etc), we need to "adjust" its
12503 * horizontal position, and that of all subsequent tags
12504 * that may be on the same line. PITA.
12505 *
12506 * [This seems to work as long as a tag isn't a line
12507 * crosser; when it is, the position of anchors on either
12508 * side of the split tag, seem to "float" and try to be
12509 * as "centered" as possible. Which means that simply
12510 * incrementing the line_pos by the fixed value of the
12511 * number of digits that got added to some tag in either
12512 * line doesn't work quite right, and the text for (say)
12513 * a button may get stomped on by another copy of itself,
12514 * but offset by a few chars, when it is selected (eg,
12515 * "Box Office" may end up looking like "BoBox Office" or
12516 * "Box Officece", etc.
12517 *
12518 * Dunno how to fix that behavior ATT, but at least the
12519 * tag numbers themselves are correct. -KED /\oo/\ ]
12520 */
12521 if ((new_n -= n) != 0) {
12522 nxt_anchor = st_anchor;
12523 while ((nxt_anchor) &&
12524 (nxt_anchor->line_num == a->line_num)) {
12525 nxt_anchor->line_pos = (short) (nxt_anchor->line_pos
12526 + new_n);
12527 nxt_anchor = nxt_anchor->next;
12528 }
12529 if (st_anchor)
12530 st_anchor = st_anchor->next;
12531 }
12532 }
12533 }
12534
12535 /*
12536 * Unfortunately, valid [tag] strings *can* be split across two
12537 * lines. Perhaps it would be best to just prevent that from
12538 * happening, but a look into that code, makes me wonder. Anyway,
12539 * we can handle such tags without *too* much trouble in here [I
12540 * think], though since such animals are rather rare, it makes it
12541 * a bit difficult to test thoroughly (ie, Beyond here, there be
12542 * Dragons).
12543 *
12544 * We use lxbuf[] to deal with the two lines involved.
12545 */
12546 pre_n = (int) strlen(p); /* count of 1st part chars in this line */
12547 post_n = (int) strlen(ht->next->data);
12548 if (plx
12549 && (pre_n + post_n + 2 < (int) sizeof(lxbuf))) {
12550 strcpy(lx, p); /* <- 1st part of a possible lx'ing tag */
12551 strcat(lx, ht->next->data); /* tack on NEXT line */
12552
12553 t = lx;
12554 n = 0;
12555 valid = TRUE;
12556
12557 /*
12558 * Go hunting again for just digits, followed by tag end ']'.
12559 */
12560 while (*t != ']') {
12561 if (isdigit(UCH(*t++))) {
12562 n++;
12563 continue;
12564 } else {
12565 valid = FALSE;
12566 break;
12567 }
12568 }
12569
12570 /*
12571 * It *looks* like a line crosser; now we value test it to
12572 * find out for sure [but see the "false match" warning,
12573 * above], and if it matches, increment it into the buffer,
12574 * along with the 2nd line's text.
12575 */
12576 if ((valid)
12577 && (n > 0)
12578 && (n + post_n + 2) < MAX_LINE) {
12579 val = atoi(lx);
12580 if ((val == *old_val) || (*old_val == 0)) {
12581 const char *r;
12582
12583 if (*old_val != 0)
12584 (*old_val)++;
12585 val += incr;
12586 sprintf(lx, "%d", val);
12587 new_n = (int) strlen(lx);
12588 if ((r = StrChr(ht->next->data, ']')) == 0) {
12589 r = "";
12590 }
12591 strcat(lx, r);
12592
12593 /*
12594 * We keep the the same number of chars from the
12595 * adjusted tag number in the current line; any
12596 * extra chars due to a digits increase, will be
12597 * stuffed into the next line.
12598 *
12599 * Keep track of any digits added, for the next
12600 * pass through.
12601 */
12602 s = StrNCpy(s, lx, pre_n) + pre_n;
12603 lx += pre_n;
12604 strcpy(ht->next->data, lx);
12605
12606 *lx_val = new_n - n;
12607 }
12608 }
12609 break; /* had an lx'er, so we're done with this line */
12610 }
12611 }
12612 }
12613
12614 *s = '\0';
12615
12616 n = (int) strlen(ht->data);
12617 if (mode == CHOP) {
12618 *(buf + n) = '\0';
12619 } else if (strlen(buf) > ht->size) {
12620 /* we didn't allocate enough space originally - increase it */
12621 HTLine *temp;
12622
12623 allocHTLine(temp, strlen(buf));
12624 if (!temp)
12625 outofmem(__FILE__, "increment_tagged_htline");
12626
12627 memcpy(temp, ht, LINE_SIZE(0));
12628 #if defined(USE_COLOR_STYLE)
12629 POOLallocstyles(temp->styles, ht->numstyles);
12630 if (!temp->styles)
12631 outofmem(__FILE__, "increment_tagged_htline");
12632 memcpy(temp->styles, ht->styles, sizeof(HTStyleChange) * ht->numstyles);
12633 #endif
12634 ht = temp;
12635 ht->prev->next = ht; /* Link in new line */
12636 ht->next->prev = ht; /* Could be same node of course */
12637 }
12638 strcpy(ht->data, buf);
12639
12640 return ((int) strlen(buf) - n + fixup);
12641 }
12642
12643 /*
12644 * Creates a new anchor and associated struct's appropriate for a form
12645 * TEXTAREA, and links them into the lists following the current anchor
12646 * position (as specified by arg1).
12647 *
12648 * Exits with arg1 now pointing at the new TextAnchor, and arg2 pointing
12649 * at the new, associated HTLine.
12650 *
12651 * --KED 02/13/99
12652 */
insert_new_textarea_anchor(TextAnchor ** curr_anchor,HTLine ** exit_htline)12653 static void insert_new_textarea_anchor(TextAnchor **curr_anchor, HTLine **exit_htline)
12654 {
12655 TextAnchor *anchor = *curr_anchor;
12656 HTLine *htline;
12657
12658 TextAnchor *a = 0;
12659 FormInfo *f = 0;
12660 HTLine *l = 0;
12661
12662 int curr_tag = 0; /* 0 ==> match any [tag] number */
12663 int lx = 0; /* 0 ==> no line crossing [tag]; it's a new line */
12664 int i;
12665
12666 /*
12667 * Find line in the text that matches ending anchorline of
12668 * the TEXTAREA.
12669 *
12670 * [Yes, Virginia ... we *do* have to go thru this for each
12671 * anchor being added, since there is NOT a 1-to-1 mapping
12672 * between anchors and htlines. I suppose we could create
12673 * YAS (Yet Another Struct), but there are too many structs{}
12674 * floating around in here, as it is. IMNSHO.]
12675 */
12676 for (htline = FirstHTLine(HTMainText), i = 0; anchor->line_num != i; i++) {
12677 htline = htline->next;
12678 if (htline == HTMainText->last_line)
12679 break;
12680 }
12681
12682 /*
12683 * Clone and initialize the struct's needed to add a new TEXTAREA
12684 * anchor.
12685 */
12686 allocHTLine(l, MAX_LINE);
12687 POOLtypecalloc(TextAnchor, a);
12688
12689 POOLtypecalloc(FormInfo, f);
12690 if (a == NULL || l == NULL || f == NULL)
12691 outofmem(__FILE__, "insert_new_textarea_anchor");
12692
12693 /* Init all the fields in the new TextAnchor. */
12694 /* [anything "special" needed based on ->show_anchor value ?] */
12695 a->next = anchor->next;
12696 a->number = anchor->number;
12697 a->line_pos = anchor->line_pos;
12698 a->extent = anchor->extent;
12699 a->sgml_offset = SGML_offset();
12700 a->line_num = anchor->line_num + 1;
12701 LYCopyHiText(a, anchor);
12702 a->link_type = anchor->link_type;
12703 a->input_field = f;
12704 a->show_anchor = anchor->show_anchor;
12705 a->inUnderline = anchor->inUnderline;
12706 a->expansion_anch = TRUE;
12707 a->anchor = NULL;
12708
12709 /* Just the (seemingly) relevant fields in the new FormInfo. */
12710 /* [do we need to do anything "special" based on ->disabled] */
12711 StrAllocCopy(f->name, anchor->input_field->name);
12712 f->number = anchor->input_field->number;
12713 f->type = anchor->input_field->type;
12714 StrAllocCopy(f->orig_value, "");
12715 f->size = anchor->input_field->size;
12716 f->maxlength = anchor->input_field->maxlength;
12717 f->no_cache = anchor->input_field->no_cache;
12718 f->disabled = anchor->input_field->disabled;
12719 f->readonly = anchor->input_field->readonly;
12720 f->value_cs = current_char_set; /* use current setting - kw */
12721
12722 /* Init all the fields in the new HTLine (but see the #if). */
12723 l->next = htline->next;
12724 l->prev = htline;
12725 l->offset = htline->offset;
12726 l->size = htline->size;
12727 #if defined(USE_COLOR_STYLE)
12728 /* dup styles[] if needed [no need in TEXTAREA (?); leave 0's] */
12729 l->numstyles = htline->numstyles;
12730 /*we fork the pointers! */
12731 l->styles = htline->styles;
12732 #endif
12733 strcpy(l->data, htline->data);
12734
12735 /*
12736 * Link in the new HTLine.
12737 */
12738 htline->next->prev = l;
12739 htline->next = l;
12740
12741 if (fields_are_numbered()) {
12742 a->number++;
12743 increment_tagged_htline(l, a, &lx, &curr_tag, 1, CHOP);
12744 }
12745
12746 /*
12747 * If we're at the tail end of the TextAnchor or HTLine list(s),
12748 * the new node becomes the last node.
12749 */
12750 if (anchor == HTMainText->last_anchor)
12751 HTMainText->last_anchor = a;
12752 if (htline == HTMainText->last_line)
12753 HTMainText->last_line = l;
12754
12755 /*
12756 * Link in the new TextAnchor and point the entry anchor arg at it;
12757 * point the entry HTLine arg at it, too.
12758 */
12759 anchor->next = a;
12760 *curr_anchor = a;
12761
12762 *exit_htline = l->next;
12763
12764 return;
12765 }
12766
12767 /*
12768 * If new anchors were added to expand a TEXTAREA, we need to ripple the
12769 * new line numbers [and char counts ?] thru the subsequent anchors.
12770 *
12771 * If form lines are getting [nnn] tagged, we need to update the displayed
12772 * tag values to match (which means rerendering them ... sigh).
12773 *
12774 * Finally, we need to update various HTMainText and other counts, etc.
12775 *
12776 * [dunno if the char counts really *need* to be done, or if we're using
12777 * the exactly proper values/algorithms ... seems to be OK though ...]
12778 *
12779 * --KED 02/13/99
12780 */
update_subsequent_anchors(int newlines,TextAnchor * start_anchor,HTLine * start_htline,int start_tag)12781 static void update_subsequent_anchors(int newlines,
12782 TextAnchor *start_anchor,
12783 HTLine *start_htline,
12784 int start_tag)
12785 {
12786 TextAnchor *anchor;
12787 HTLine *htline = start_htline;
12788
12789 int line_adj = 0;
12790 int tag_adj = 0;
12791 int lx = 0;
12792 int hang = 0; /* for HANG detection of a nasty intermittent */
12793 int hang_detect = 100000; /* ditto */
12794
12795 CTRACE((tfp, "GridText: adjusting struct's to add %d new line(s)\n", newlines));
12796
12797 /*
12798 * Update numeric fields of the rest of the anchors.
12799 *
12800 * [We bypass bumping ->number if it has a value of 0, which takes care
12801 * of the ->input_field->type == F_HIDDEN_TYPE (as well as any other
12802 * "hidden" anchors, if such things exist). Seems like the "right
12803 * thing" to do. I think.]
12804 */
12805 anchor = start_anchor->next; /* begin updating with the NEXT anchor */
12806 while (anchor) {
12807 if (fields_are_numbered() &&
12808 (anchor->number != 0))
12809 anchor->number += newlines;
12810 anchor->line_num += newlines;
12811 anchor = anchor->next;
12812 }
12813
12814 /*
12815 * Update/rerender anchor [tags], if they are being numbered.
12816 *
12817 * [If a number tag (eg, "[177]") is itself broken across a line
12818 * boundary, this fixup only partially works. While the tag
12819 * numbering is done properly across the pair of lines, the
12820 * horizontal positioning on *either* side of the split, can get
12821 * out of sync by a char or two when it gets selected. See the
12822 * [comments] in increment_tagged_htline() for some more detail.
12823 *
12824 * I suppose THE fix is to prevent such tag-breaking in the first
12825 * place (dunno where yet, though). Ah well ... at least the tag
12826 * numbers themselves are correct from top to bottom now.
12827 *
12828 * All that said, about the only time this will be a problem in
12829 * *practice*, is when a page has near 1000 links or more (possibly
12830 * after a TEXTAREA expansion), and has line crossing tag(s), and
12831 * the tag numbers in a line crosser go from initially all 3 digit
12832 * numbers, to some mix of 3 and 4 digits (or all 4 digits) as a
12833 * result of the expansion process. Oh, you also need a "clump" of
12834 * anchors all on the same lines.
12835 *
12836 * Yes, it *can* happen, but in real life, it probably won't be
12837 * seen very much ...]
12838 *
12839 * [This may also be an artifact of bumping into the right hand
12840 * screen edge (or RHS margin), since we don't even *think* about
12841 * relocating an anchor to the following line, when [tag] digits
12842 * expansion pushes things too far in that direction.]
12843 */
12844 if (fields_are_numbered()) {
12845 anchor = start_anchor->next;
12846 while (htline != FirstHTLine(HTMainText)) {
12847
12848 while (anchor) {
12849 if ((anchor->number - newlines) == start_tag)
12850 break;
12851
12852 /*** A HANG (infinite loop) *has* occurred here, with */
12853 /*** the values of anchor and anchor->next being the */
12854 /*** the same, OR with anchor->number "magically" and */
12855 /*** suddenly taking on an anchor-pointer-like value. */
12856 /*** */
12857 /*** The same code and same doc have both passed and */
12858 /*** failed at different times, which indicates some */
12859 /*** sort of content/html dependency, or some kind of */
12860 /*** a "race" condition, but I'll be damned if I can */
12861 /*** find it after tons of CTRACE's, printf()'s, gdb */
12862 /*** breakpoints and watchpoints, etc. */
12863 /*** */
12864 /*** I have added a hang detector (with error msg and */
12865 /*** beep) here, to break the loop and warn the user, */
12866 /*** until it can be isolated and fixed. */
12867 /*** */
12868 /*** [One UGLY intermittent .. gak ..! 02/22/99 KED] */
12869
12870 hang++;
12871 if ((anchor == anchor->next) || (hang >= hang_detect))
12872 goto hang_detected;
12873
12874 anchor = anchor->next;
12875 }
12876
12877 if (anchor) {
12878 line_adj = increment_tagged_htline(htline, anchor, &lx,
12879 &start_tag, newlines,
12880 NOCHOP);
12881 htline->size = (unsigned short) (htline->size + line_adj);
12882 tag_adj += line_adj;
12883
12884 } else {
12885
12886 break; /* out of anchors ... we're done */
12887 }
12888
12889 htline = htline->next;
12890 }
12891 }
12892
12893 finish:
12894 /*
12895 * Fixup various global variables.
12896 */
12897 nlinks += newlines;
12898 HTMainText->Lines += newlines;
12899 HTMainText->last_anchor_number += newlines;
12900
12901 more_text = HText_canScrollDown();
12902
12903 CTRACE((tfp, "GridText: TextAnchor and HTLine struct's adjusted\n"));
12904
12905 return;
12906
12907 hang_detected: /* ugliness has happened; inform user and do the best we can */
12908
12909 HTAlert(gettext("Hang Detect: TextAnchor struct corrupted - suggest aborting!"));
12910 goto finish;
12911 }
12912
12913 /*
12914 * Check if the given anchor is a TEXTAREA belonging to the given form.
12915 *
12916 * KED's note -
12917 * [Finding the TEXTAREA we're actually *in* with these attributes isn't
12918 * foolproof. The form number isn't unique to a given TEXTAREA, and there
12919 * *could* be TEXTAREA's with the same "name". If that should ever be true,
12920 * we'll actually get the data from the *1st* TEXTAREA in the page that
12921 * matches. We should probably assign a unique id to each TEXTAREA in a page,
12922 * and match on that, to avoid this (potential) problem.
12923 *
12924 * Since the odds of "false matches" *actually* happening in real life seem
12925 * rather small though, we'll hold off doing this, for a rainy day ...]
12926 */
IsFormsTextarea(FormInfo * form,TextAnchor * anchor_ptr)12927 static BOOLEAN IsFormsTextarea(FormInfo * form, TextAnchor *anchor_ptr)
12928 {
12929 return (BOOLEAN) ((anchor_ptr->link_type == INPUT_ANCHOR) &&
12930 (anchor_ptr->input_field->type == F_TEXTAREA_TYPE) &&
12931 (anchor_ptr->input_field->number == form->number) &&
12932 !strcmp(anchor_ptr->input_field->name, form->name));
12933 }
12934
readEditedFile(char * ed_temp)12935 static char *readEditedFile(char *ed_temp)
12936 {
12937 struct stat stat_info;
12938 size_t size;
12939
12940 FILE *fp;
12941
12942 char *ebuf;
12943
12944 CTRACE((tfp, "GridText: entered HText_EditTextArea()\n"));
12945
12946 /*
12947 * Read back the edited temp file into our buffer.
12948 */
12949 if ((stat(ed_temp, &stat_info) < 0) ||
12950 !S_ISREG(stat_info.st_mode) ||
12951 ((size = (size_t) stat_info.st_size) == 0)) {
12952 size = 0;
12953 ebuf = typecalloc(char);
12954
12955 if (!ebuf)
12956 outofmem(__FILE__, "HText_EditTextArea");
12957 } else {
12958 ebuf = typecallocn(char, size + 1);
12959
12960 if (!ebuf) {
12961 /*
12962 * This could be huge - don't exit if we don't have enough
12963 * memory for it. With some luck, the user may be even able
12964 * to recover the file manually from the temp space while
12965 * the lynx session is not over. - kw
12966 */
12967 HTAlwaysAlert(NULL, MEMORY_EXHAUSTED_FILE);
12968 return 0;
12969 }
12970
12971 if ((fp = fopen(ed_temp, "r")) != 0) {
12972 size = fread(ebuf, (size_t) 1, size, fp);
12973 LYCloseInput(fp);
12974 ebuf[size] = '\0'; /* Terminate! - kw */
12975 } else {
12976 size = 0;
12977 }
12978 }
12979
12980 /*
12981 * Nuke any blank lines from the end of the edited data.
12982 */
12983 while ((size != 0)
12984 && (CanTrimTextArea(UCH(ebuf[size - 1])) || (ebuf[size - 1] == '\0')))
12985 ebuf[--size] = '\0';
12986
12987 return ebuf;
12988 }
12989
finish_ExtEditForm(LinkInfo * form_link,TextAnchor * start_anchor,char * ed_temp,int orig_cnt)12990 static int finish_ExtEditForm(LinkInfo * form_link, TextAnchor *start_anchor,
12991 char *ed_temp,
12992 int orig_cnt)
12993 {
12994 TextAnchor *anchor_ptr;
12995 TextAnchor *end_anchor = NULL;
12996 BOOLEAN wrapalert = FALSE;
12997
12998 int entry_line = form_link->anchor_line_num;
12999 int exit_line = 0;
13000 int line_cnt = 1;
13001
13002 HTLine *htline = NULL;
13003
13004 char *ebuf;
13005 char *line;
13006 char *lp;
13007 char *cp;
13008 int match_tag = 0;
13009 int newlines = 0;
13010 int len, len0;
13011 int display_size;
13012 int wanted_fieldlen_wrap = -1; /* not yet asked; 0 means don't. */
13013 char *skip_at = NULL;
13014 int skip_num = 0, i;
13015 size_t line_used = MAX_LINE;
13016
13017 CTRACE((tfp, "GridText: entered HText_EditTextArea()\n"));
13018
13019 if ((ebuf = readEditedFile(ed_temp)) == 0) {
13020 return 0;
13021 }
13022
13023 /*
13024 * Copy each line from the temp file into the corresponding anchor
13025 * struct. Add new lines to the TEXTAREA if needed. (Always leave
13026 * the user with a blank line at the end of the TEXTAREA.)
13027 */
13028 if ((line = typeMallocn(char, line_used)) == 0)
13029 outofmem(__FILE__, "HText_EditTextArea");
13030
13031 anchor_ptr = start_anchor;
13032 display_size = anchor_ptr->input_field->size;
13033 if (display_size <= 4 ||
13034 display_size >= MAX_LINE)
13035 wanted_fieldlen_wrap = 0;
13036
13037 len = 0;
13038 lp = ebuf;
13039
13040 while ((line_cnt <= orig_cnt) || (*lp) || ((len != 0) && (*lp == '\0'))) {
13041
13042 if (skip_at) {
13043 len0 = (int) (skip_at - lp);
13044 LYStrNCpy(line, lp, len0);
13045 lp = skip_at + skip_num;
13046 skip_at = NULL;
13047 skip_num = 0;
13048
13049 assert(lp != NULL);
13050 } else {
13051 len0 = 0;
13052 }
13053 line[len0] = '\0';
13054
13055 if ((cp = StrChr(lp, '\n')) != 0)
13056 len = (int) (cp - lp);
13057 else
13058 len = (int) strlen(lp);
13059
13060 if (wanted_fieldlen_wrap < 0 &&
13061 !wrapalert &&
13062 len0 + len >= display_size &&
13063 (cp = StrChr(lp, ' ')) != NULL &&
13064 (cp - lp) < display_size - 1) {
13065
13066 LYFixCursesOn("ask for confirmation:");
13067 LYerase(); /* don't show previous state */
13068 if (HTConfirmDefault(gettext("Wrap lines to fit displayed area?"),
13069 NO)) {
13070 wanted_fieldlen_wrap = display_size - 1;
13071 } else {
13072 wanted_fieldlen_wrap = 0;
13073 }
13074 }
13075
13076 if (wanted_fieldlen_wrap > 0 &&
13077 len0 + len > wanted_fieldlen_wrap) {
13078
13079 for (i = wanted_fieldlen_wrap - len0;
13080 i + len0 >= wanted_fieldlen_wrap / 4; i--) {
13081
13082 if (isspace(UCH(lp[i]))) {
13083 len = i + 1;
13084 cp = lp + i;
13085 if (cp[1] != '\n' &&
13086 isspace(UCH(cp[1])) &&
13087 !isspace(UCH(cp[2]))) {
13088 len++;
13089 cp++;
13090 }
13091 if (!isspace(UCH(cp[1]))) {
13092 while (*cp && *cp != '\r' && *cp != '\n' &&
13093 (cp - lp) <= len + (3 * wanted_fieldlen_wrap / 4))
13094 cp++; /* search for next line break */
13095 if (*cp == '\r' && cp[1] == '\n')
13096 cp++;
13097 if (*cp == '\n' &&
13098 (cp[1] == '\r' || cp[1] == '\n' ||
13099 !isspace(UCH(cp[1])))) {
13100 *cp = ' ';
13101 while (isspace(UCH(*(cp - 1)))) {
13102 skip_num++;
13103 cp--;
13104 }
13105 skip_at = cp;
13106 }
13107 }
13108 break;
13109 }
13110 }
13111 }
13112
13113 if (wanted_fieldlen_wrap > 0 &&
13114 (len0 + len) > wanted_fieldlen_wrap) {
13115
13116 i = len - 1;
13117 while (len0 + i + 1 > wanted_fieldlen_wrap &&
13118 isspace(UCH(lp[i])))
13119 i--;
13120 if (len0 + i + 1 > wanted_fieldlen_wrap)
13121 len = wanted_fieldlen_wrap - len0;
13122 }
13123
13124 /*
13125 * Check if the new text will fit in the buffer. HTML does not define
13126 * a "maxlength" attribute for TEXTAREA; its data can grow as needed.
13127 * Lynx will not adjust the display to reflect larger amounts of text;
13128 * it relies on the rows/cols attributes as well as the initial content
13129 * of the text area for the layout.
13130 */
13131 if ((size_t) (len0 + len) >= line_used) {
13132 line_used = (size_t) (3 * (len0 + len)) / 2;
13133 if ((line = typeRealloc(char, line, line_used)) == 0)
13134 outofmem(__FILE__, "HText_EditTextArea");
13135 }
13136
13137 strncat(line, lp, (size_t) len);
13138 *(line + len0 + len) = '\0';
13139
13140 /*
13141 * If there are more lines in the edit buffer than were in the
13142 * original TEXTAREA, we need to add a new line/anchor, continuing
13143 * on until the edit buffer is empty.
13144 */
13145 if (line_cnt > orig_cnt) {
13146 insert_new_textarea_anchor(&end_anchor, &htline);
13147
13148 assert(end_anchor != NULL);
13149 assert(end_anchor->input_field != NULL);
13150
13151 anchor_ptr = end_anchor; /* make the new anchor current */
13152 newlines++;
13153 }
13154
13155 assert(anchor_ptr != NULL);
13156
13157 /*
13158 * Finally copy the new line from the edit buffer into the anchor.
13159 */
13160 StrAllocCopy(anchor_ptr->input_field->value, line);
13161
13162 /*
13163 * Keep track of 1st blank line in any trailing blank lines, for
13164 * later cursor repositioning.
13165 */
13166 if (len0 + len > 0)
13167 exit_line = 0;
13168 else if (exit_line == 0)
13169 exit_line = anchor_ptr->line_num;
13170
13171 /*
13172 * And do the next line of edited text, for the next anchor ...
13173 */
13174 lp += len;
13175 if (*lp && isspace(UCH(*lp)))
13176 lp++;
13177
13178 end_anchor = anchor_ptr;
13179 anchor_ptr = anchor_ptr->next;
13180
13181 if (anchor_ptr)
13182 match_tag = anchor_ptr->number;
13183
13184 line_cnt++;
13185 }
13186
13187 CTRACE((tfp, "GridText: edited text inserted into lynx struct's\n"));
13188
13189 /*
13190 * If we've added any new lines/anchors, we need to adjust various
13191 * things in all anchor-bearing lines following the last newly added
13192 * line/anchor. The fun stuff starts here ...
13193 */
13194 if (newlines > 0)
13195 update_subsequent_anchors(newlines, end_anchor, htline, match_tag);
13196
13197 /*
13198 * Cleanup time.
13199 */
13200 FREE(line);
13201 FREE(ebuf);
13202
13203 /*
13204 * Return the offset needed to move the cursor from its current
13205 * (on entry) line number, to the 1st blank line of the trailing
13206 * (group of) blank line(s), which is where we want to be. Let
13207 * the caller deal with moving us there, however ... :-) ...
13208 */
13209 return (exit_line - entry_line);
13210 }
13211
13212 /*
13213 * Transfer the initial contents of a TEXTAREA to a temp file, invoke the
13214 * user's editor on that file, then transfer the contents of the resultant
13215 * edited file back into the TEXTAREA (expanding the size of the area, if
13216 * required).
13217 *
13218 * Returns the number of lines that the cursor should be moved so that it
13219 * will end up on the 1st blank line of whatever number of trailing blank
13220 * lines there are in the TEXTAREA (there will *always* be at least one).
13221 *
13222 * --KED 02/01/99
13223 */
HText_EditTextArea(LinkInfo * form_link)13224 int HText_EditTextArea(LinkInfo * form_link)
13225 {
13226 char *ed_temp;
13227 FILE *fp;
13228
13229 TextAnchor *anchor_ptr;
13230 TextAnchor *start_anchor = NULL;
13231 BOOLEAN firstanchor = TRUE;
13232
13233 char ed_offset[DigitsOf(int) + 3];
13234 int start_line = 0;
13235 int entry_line = form_link->anchor_line_num;
13236 int orig_cnt = 0;
13237 int offset = 0;
13238
13239 FormInfo *form = form_link->l_form;
13240
13241 CTRACE((tfp, "GridText: entered HText_EditTextArea()\n"));
13242
13243 if ((ed_temp = typeMallocn(char, LY_MAXPATH)) == 0) {
13244 outofmem(__FILE__, "HText_EditTextArea");
13245 } else if ((fp = LYOpenTemp(ed_temp, "", "w")) != 0) {
13246
13247 /*
13248 * Begin at the beginning, to find 1st anchor in the TEXTAREA, then
13249 * write all of its lines (anchors) out to the edit temp file.
13250 */
13251 anchor_ptr = HTMainText->first_anchor;
13252
13253 while (anchor_ptr) {
13254
13255 if (IsFormsTextarea(form, anchor_ptr)) {
13256
13257 if (firstanchor) {
13258 firstanchor = FALSE;
13259 start_anchor = anchor_ptr;
13260 start_line = anchor_ptr->line_num;
13261 }
13262 orig_cnt++;
13263
13264 /*
13265 * Write the anchors' text to the temp edit file.
13266 */
13267 fputs(anchor_ptr->input_field->value, fp);
13268 fputc('\n', fp);
13269
13270 } else {
13271
13272 if (!firstanchor)
13273 break;
13274 }
13275 anchor_ptr = anchor_ptr->next;
13276 }
13277 LYCloseTempFP(fp);
13278
13279 if (start_anchor != 0) {
13280 CTRACE((tfp, "GridText: TEXTAREA name=|%s| dumped to tempfile\n", form->name));
13281 CTRACE((tfp, "GridText: invoking editor (%s) on tempfile\n", editor));
13282
13283 /*
13284 * Go edit the TEXTAREA temp file, with the initial editor line
13285 * corresponding to the TEXTAREA line the cursor is on (if such
13286 * positioning is supported by the editor [as lynx knows it]).
13287 */
13288 ed_offset[0] = 0; /* pre-ANSI compilers don't initialize aggregates - TD */
13289 if (((entry_line - start_line) > 0) && editor_can_position())
13290 sprintf(ed_offset, "%d", ((entry_line - start_line) + 1));
13291
13292 edit_temporary_file(ed_temp, ed_offset, NULL);
13293
13294 CTRACE((tfp, "GridText: returned from editor (%s)\n", editor));
13295
13296 if (!form->disabled)
13297 offset = finish_ExtEditForm(form_link, start_anchor, ed_temp, orig_cnt);
13298
13299 CTRACE((tfp, "GridText: exiting HText_EditTextArea()\n"));
13300 }
13301 (void) LYRemoveTemp(ed_temp);
13302 FREE(ed_temp);
13303 }
13304
13305 /*
13306 * Return the offset needed to move the cursor from its current
13307 * (on entry) line number, to the 1st blank line of the trailing
13308 * (group of) blank line(s), which is where we want to be. Let
13309 * the caller deal with moving us there, however ... :-) ...
13310 */
13311 return offset;
13312 }
13313
13314 /*
13315 * Similar to HText_EditTextArea, but assume a single-line text field -TD
13316 */
HText_EditTextField(LinkInfo * form_link)13317 void HText_EditTextField(LinkInfo * form_link)
13318 {
13319 char *ed_temp;
13320 FILE *fp;
13321
13322 FormInfo *form = form_link->l_form;
13323
13324 CTRACE((tfp, "GridText: entered HText_EditTextField()\n"));
13325
13326 ed_temp = typeMallocn(char, LY_MAXPATH);
13327
13328 if ((fp = LYOpenTemp(ed_temp, "", "w")) == 0) {
13329 FREE(ed_temp);
13330 return;
13331 }
13332
13333 /*
13334 * Write the anchors' text to the temp edit file.
13335 */
13336 fputs(form->value, fp);
13337 fputc('\n', fp);
13338
13339 LYCloseTempFP(fp);
13340
13341 CTRACE((tfp, "GridText: text field |%s| dumped to tempfile\n", form_link->lname));
13342 CTRACE((tfp, "GridText: invoking editor (%s) on tempfile\n", editor));
13343
13344 edit_temporary_file(ed_temp, "", NULL);
13345
13346 CTRACE((tfp, "GridText: returned from editor (%s)\n", editor));
13347
13348 if (!form->disabled) {
13349 char *ebuf;
13350 char *p;
13351
13352 if ((ebuf = readEditedFile(ed_temp)) != 0) {
13353 /*
13354 * Only use the first line of the result.
13355 */
13356 for (p = ebuf; *p != '\0'; ++p) {
13357 if (*p == '\n' || *p == '\r') {
13358 *p = '\0';
13359 break;
13360 }
13361 }
13362 StrAllocCopy(form->value, ebuf);
13363 FREE(ebuf);
13364 }
13365 }
13366
13367 (void) LYRemoveTemp(ed_temp);
13368 FREE(ed_temp);
13369
13370 CTRACE((tfp, "GridText: exiting HText_EditTextField()\n"));
13371 }
13372
13373 /*
13374 * Expand the size of a TEXTAREA by a fixed number of lines (as specified
13375 * by arg2).
13376 *
13377 * --KED 02/14/99
13378 */
HText_ExpandTextarea(LinkInfo * form_link,int newlines)13379 void HText_ExpandTextarea(LinkInfo * form_link, int newlines)
13380 {
13381 TextAnchor *anchor_ptr;
13382 TextAnchor *end_anchor = NULL;
13383 BOOLEAN firstanchor = TRUE;
13384
13385 FormInfo *form = form_link->l_form;
13386
13387 HTLine *htline = NULL;
13388
13389 int match_tag = 0;
13390 int i;
13391
13392 CTRACE((tfp, "GridText: entered HText_ExpandTextarea()\n"));
13393
13394 if (newlines < 1)
13395 return;
13396
13397 /*
13398 * Begin at the beginning, to find the TEXTAREA, then on to find
13399 * the last line (anchor) in it.
13400 */
13401 anchor_ptr = HTMainText->first_anchor;
13402
13403 while (anchor_ptr) {
13404
13405 if (IsFormsTextarea(form, anchor_ptr)) {
13406
13407 if (firstanchor)
13408 firstanchor = FALSE;
13409
13410 end_anchor = anchor_ptr;
13411
13412 } else {
13413
13414 if (!firstanchor)
13415 break;
13416 }
13417 anchor_ptr = anchor_ptr->next;
13418 }
13419
13420 for (i = 1; i <= newlines; i++) {
13421 insert_new_textarea_anchor(&end_anchor, &htline);
13422
13423 /*
13424 * Make the new line blank.
13425 */
13426 StrAllocCopy(end_anchor->input_field->value, "");
13427
13428 /*
13429 * And go add another line ...
13430 */
13431 if (end_anchor->next)
13432 match_tag = end_anchor->next->number;
13433 }
13434
13435 CTRACE((tfp, "GridText: %d blank line(s) added to TEXTAREA name=|%s|\n",
13436 newlines, form->name));
13437
13438 /*
13439 * We need to adjust various things in all anchor bearing lines
13440 * following the last newly added line/anchor. Fun stuff.
13441 */
13442 update_subsequent_anchors(newlines, end_anchor, htline, match_tag);
13443
13444 CTRACE((tfp, "GridText: exiting HText_ExpandTextarea()\n"));
13445
13446 return;
13447 }
13448
13449 /*
13450 * Insert the contents of a file into a TEXTAREA between the cursor line,
13451 * and the line preceding it.
13452 *
13453 * Returns the number of lines that the cursor should be moved so that it
13454 * will end up on the 1st line in the TEXTAREA following the inserted file
13455 * (if we decide to do that).
13456 *
13457 * --KED 02/21/99
13458 */
HText_InsertFile(LinkInfo * form_link)13459 int HText_InsertFile(LinkInfo * form_link)
13460 {
13461 struct stat stat_info;
13462 size_t size;
13463
13464 FILE *fp;
13465 char *fn;
13466
13467 TextAnchor *anchor_ptr;
13468 TextAnchor *prev_anchor = NULL;
13469 TextAnchor *end_anchor = NULL;
13470 BOOLEAN firstanchor = TRUE;
13471 BOOLEAN truncalert = FALSE;
13472
13473 FormInfo *form = form_link->l_form;
13474
13475 HTLine *htline = NULL;
13476
13477 TextAnchor *a = 0;
13478 FormInfo *f = 0;
13479 HTLine *l = 0;
13480
13481 char *fbuf = 0;
13482 char *line = 0;
13483 char *lp;
13484 char *cp;
13485 int entry_line = form_link->anchor_line_num;
13486 int file_cs;
13487 int match_tag = 0;
13488 int newlines = 0;
13489 int len;
13490 int i;
13491
13492 CTRACE((tfp, "GridText: entered HText_InsertFile()\n"));
13493
13494 /*
13495 * Get the filename of the insert file.
13496 */
13497 if (!(fn = GetFileName())) {
13498 HTInfoMsg(FILE_INSERT_CANCELLED);
13499 CTRACE((tfp,
13500 "GridText: file insert cancelled - no filename provided\n"));
13501 return (0);
13502 }
13503 if (no_dotfiles || !show_dotfiles) {
13504 if (*LYPathLeaf(fn) == '.') {
13505 HTUserMsg(FILENAME_CANNOT_BE_DOT);
13506 return (0);
13507 }
13508 }
13509
13510 /*
13511 * Read it into our buffer (abort on 0-length file).
13512 */
13513 if ((stat(fn, &stat_info) < 0) ||
13514 ((size = (size_t) stat_info.st_size) == 0)) {
13515 HTInfoMsg(FILE_INSERT_0_LENGTH);
13516 CTRACE((tfp,
13517 "GridText: file insert aborted - file=|%s|- was 0-length\n",
13518 fn));
13519 FREE(fn);
13520 return (0);
13521
13522 } else {
13523
13524 if ((fbuf = typecallocn(char, size + 1)) == NULL) {
13525 /*
13526 * This could be huge - don't exit if we don't have enough
13527 * memory for it. - kw
13528 */
13529 free(fn);
13530 HTAlert(MEMORY_EXHAUSTED_FILE);
13531 return 0;
13532 }
13533
13534 /* Try to make the same assumption for the charset of the inserted
13535 * file as we would for normal loading of that file, i.e. taking
13536 * assume_local_charset and suffix mappings into account.
13537 * If there is a mismatch with the display character set, characters
13538 * may be displayed wrong, too bad; but the user has a chance to
13539 * correct this by editing the lines, which will update f->value_cs
13540 * again. - kw
13541 */
13542 LYGetFileInfo(fn, 0, 0, 0, 0, 0, &file_cs);
13543
13544 fp = fopen(fn, "r");
13545 if (!fp) {
13546 free(fbuf);
13547 free(fn);
13548 HTAlert(FILE_CANNOT_OPEN_R);
13549 return 0;
13550 }
13551 size = fread(fbuf, (size_t) 1, size, fp);
13552 LYCloseInput(fp);
13553 FREE(fn);
13554 fbuf[size] = '\0'; /* Terminate! - kw */
13555 }
13556
13557 /*
13558 * Begin at the beginning, to find the TEXTAREA we're in, then
13559 * the current cursorline.
13560 */
13561 anchor_ptr = HTMainText->first_anchor;
13562
13563 while (anchor_ptr) {
13564
13565 if (IsFormsTextarea(form, anchor_ptr)) {
13566 if (anchor_ptr->line_num == entry_line)
13567 break;
13568 }
13569 prev_anchor = anchor_ptr;
13570 anchor_ptr = anchor_ptr->next;
13571 }
13572
13573 if (anchor_ptr == NULL) {
13574 CTRACE((tfp, "BUG: could not find anchor for TEXTAREA.\n"));
13575 FREE(line);
13576 FREE(fbuf);
13577 return 0;
13578 }
13579
13580 /*
13581 * Clone a new TEXTAREA line/anchor using the cursorline anchor as
13582 * a template, but link it in BEFORE the cursorline anchor/htline.
13583 *
13584 * [We can probably combine this with insert_new_textarea_anchor()
13585 * along with a flag to indicate "insert before" as we do here,
13586 * or the "normal" mode of operation (add after "current" anchor/
13587 * line). Beware of the differences ... some are a bit subtle to
13588 * notice.]
13589 */
13590 for (htline = FirstHTLine(HTMainText), i = 0;
13591 anchor_ptr->line_num != i; i++) {
13592 htline = htline->next;
13593 if (htline == HTMainText->last_line)
13594 break;
13595 }
13596
13597 allocHTLine(l, MAX_LINE);
13598 POOLtypecalloc(TextAnchor, a);
13599
13600 POOLtypecalloc(FormInfo, f);
13601 if (a == NULL || l == NULL || f == NULL)
13602 outofmem(__FILE__, "HText_InsertFile");
13603
13604 /* Init all the fields in the new TextAnchor. */
13605 /* [anything "special" needed based on ->show_anchor value ?] */
13606 /* *INDENT-EQLS* */
13607 a->next = anchor_ptr;
13608 a->number = anchor_ptr->number;
13609 a->show_number = anchor_ptr->show_number;
13610 a->line_pos = anchor_ptr->line_pos;
13611 a->extent = anchor_ptr->extent;
13612 a->sgml_offset = SGML_offset();
13613 a->line_num = anchor_ptr->line_num;
13614 LYCopyHiText(a, anchor_ptr);
13615 a->link_type = anchor_ptr->link_type;
13616 a->input_field = f;
13617 a->show_anchor = anchor_ptr->show_anchor;
13618 a->inUnderline = anchor_ptr->inUnderline;
13619 a->expansion_anch = TRUE;
13620 a->anchor = NULL;
13621
13622 /* Just the (seemingly) relevant fields in the new FormInfo. */
13623 /* [do we need to do anything "special" based on ->disabled] */
13624 StrAllocCopy(f->name, anchor_ptr->input_field->name);
13625 f->number = anchor_ptr->input_field->number;
13626 f->type = anchor_ptr->input_field->type;
13627 StrAllocCopy(f->orig_value, "");
13628 f->size = anchor_ptr->input_field->size;
13629 f->maxlength = anchor_ptr->input_field->maxlength;
13630 f->no_cache = anchor_ptr->input_field->no_cache;
13631 f->disabled = anchor_ptr->input_field->disabled;
13632 f->readonly = anchor_ptr->input_field->readonly;
13633 f->value_cs = (file_cs >= 0) ? file_cs : current_char_set;
13634
13635 /* Init all the fields in the new HTLine (but see the #if). */
13636 l->offset = htline->offset;
13637 l->size = htline->size;
13638 #if defined(USE_COLOR_STYLE)
13639 /* dup styles[] if needed [no need in TEXTAREA (?); leave 0's] */
13640 l->numstyles = htline->numstyles;
13641 /*we fork the pointers! */
13642 l->styles = htline->styles;
13643 #endif
13644 strcpy(l->data, htline->data);
13645
13646 /*
13647 * If we're at the head of the TextAnchor list, the new node becomes
13648 * the first node.
13649 */
13650 if (anchor_ptr == HTMainText->first_anchor)
13651 HTMainText->first_anchor = a;
13652
13653 /*
13654 * Link in the new TextAnchor, and corresponding HTLine.
13655 */
13656 if (prev_anchor)
13657 prev_anchor->next = a;
13658
13659 htline = htline->prev;
13660 l->next = htline->next;
13661 l->prev = htline;
13662 htline->next->prev = l;
13663 htline->next = l;
13664
13665 /*
13666 * update_subsequent_anchors() expects htline to point to 1st potential
13667 * line needing fixup; we need to do this just in case the inserted file
13668 * was only a single line (yes, it's pathological ... ).
13669 */
13670 htline = htline->next; /* ->new (current) htline, for 1st inserted line */
13671 htline = htline->next; /* ->1st potential (following) [tag] fixup htline */
13672
13673 anchor_ptr = a;
13674 newlines++;
13675
13676 /*
13677 * Copy each line from the insert file into the corresponding anchor
13678 * struct.
13679 *
13680 * Begin with the new line/anchor we just added (above the cursorline).
13681 */
13682 if ((line = typeMallocn(char, MAX_LINE)) == 0)
13683 outofmem(__FILE__, "HText_InsertFile");
13684
13685 match_tag = anchor_ptr->number;
13686
13687 lp = fbuf;
13688
13689 while (*lp) {
13690
13691 if ((cp = StrChr(lp, '\n')) != 0)
13692 len = (int) (cp - lp);
13693 else
13694 len = (int) strlen(lp);
13695
13696 if (len >= MAX_LINE) {
13697 if (!truncalert) {
13698 HTAlert(gettext("Very long lines have been truncated!"));
13699 truncalert = TRUE;
13700 }
13701 len = MAX_LINE - 1;
13702 if (lp[len])
13703 lp[len + 1] = '\0'; /* prevent next iteration */
13704 }
13705 LYStrNCpy(line, lp, len);
13706
13707 /*
13708 * If not the first line from the insert file, we need to add
13709 * a new line/anchor, continuing on until the buffer is empty.
13710 */
13711 if (!firstanchor) {
13712 insert_new_textarea_anchor(&end_anchor, &htline);
13713 anchor_ptr = end_anchor; /* make the new anchor current */
13714 newlines++;
13715 }
13716
13717 /*
13718 * Copy the new line from the buffer into the anchor.
13719 */
13720 StrAllocCopy(anchor_ptr->input_field->value, line);
13721
13722 /*
13723 * insert_new_textarea_anchor always uses current_char_set,
13724 * we may want something else, so fix it up. - kw
13725 */
13726 if (file_cs >= 0)
13727 anchor_ptr->input_field->value_cs = file_cs;
13728
13729 /*
13730 * And do the next line of insert text, for the next anchor ...
13731 */
13732 lp += len;
13733 if (*lp)
13734 lp++;
13735
13736 firstanchor = FALSE;
13737 end_anchor = anchor_ptr;
13738 anchor_ptr = anchor_ptr->next;
13739 }
13740
13741 CTRACE((tfp, "GridText: file inserted into lynx struct's\n"));
13742
13743 /*
13744 * Now adjust various things in all anchor-bearing lines following the
13745 * last newly added line/anchor. Some say this is the fun part ...
13746 */
13747 update_subsequent_anchors(newlines, end_anchor, htline, match_tag);
13748
13749 /*
13750 * Cleanup time.
13751 */
13752 FREE(line);
13753 FREE(fbuf);
13754
13755 CTRACE((tfp, "GridText: exiting HText_InsertFile()\n"));
13756
13757 return (newlines);
13758 }
13759
13760 #ifdef USE_COLOR_STYLE
GetColumn(void)13761 static int GetColumn(void)
13762 {
13763 int result;
13764
13765 #ifdef USE_SLANG
13766 result = SLsmg_get_column();
13767 #else
13768 int y, x;
13769
13770 LYGetYX(y, x);
13771 result = x;
13772 (void) y;
13773 #endif
13774 return result;
13775 }
13776
DidWrap(int y0,int x0)13777 static BOOL DidWrap(int y0, int x0)
13778 {
13779 BOOL result = NO;
13780
13781 #ifndef USE_SLANG
13782 int y, x;
13783
13784 LYGetYX(y, x);
13785 (void) x0;
13786 if (x >= DISPLAY_COLS || ((x == 0) && (y != y0)))
13787 result = YES;
13788 #endif
13789 return result;
13790 }
13791 #endif /* USE_COLOR_STYLE */
13792
13793 /*
13794 * This function draws the part of line 'line', pointed by 'str' (which can be
13795 * non terminated with null - i.e., is line->data+N) drawing 'len' bytes (not
13796 * characters) of it. It doesn't check whether the 'len' bytes crosses a
13797 * character boundary (if multibyte chars are in string). Assumes that the
13798 * cursor is positioned in the place where the 1st char of string should be
13799 * drawn.
13800 *
13801 * This code is based on display_line. This code was tested with ncurses only
13802 * (since no support for lss is availble for Slang) -HV.
13803 */
13804 #ifdef USE_COLOR_STYLE
redraw_part_of_line(HTLine * line,const char * str,int len,HText * text)13805 static void redraw_part_of_line(HTLine *line, const char *str,
13806 int len,
13807 HText *text)
13808 {
13809 register int i;
13810 char buffer[7];
13811 const char *data, *end_of_data;
13812 size_t utf_extra = 0;
13813
13814 #ifdef USE_COLOR_STYLE
13815 int current_style = 0;
13816 int tcols, scols;
13817 #endif
13818 char LastDisplayChar = ' ';
13819 int YP, XP;
13820
13821 LYGetYX(YP, XP);
13822
13823 i = XP;
13824
13825 /* Set up the multibyte character buffer */
13826 buffer[0] = buffer[1] = buffer[2] = '\0';
13827
13828 data = str;
13829 end_of_data = data + len;
13830 i++;
13831
13832 /* this assumes that the part of line to be drawn fits in the screen */
13833 while (data < end_of_data) {
13834 buffer[0] = *data;
13835 data++;
13836
13837 #if defined(USE_COLOR_STYLE)
13838 #define CStyle line->styles[current_style]
13839
13840 tcols = GetColumn();
13841 scols = StyleToCols(text, line, current_style);
13842
13843 while (current_style < line->numstyles &&
13844 tcols >= scols) {
13845 LynxChangeStyle(CStyle.sc_style, CStyle.sc_direction);
13846 current_style++;
13847 scols = StyleToCols(text, line, current_style);
13848 }
13849 #endif
13850 switch (buffer[0]) {
13851
13852 #ifndef USE_COLOR_STYLE
13853 case LY_UNDERLINE_START_CHAR:
13854 if (dump_output_immediately && use_underscore) {
13855 LYaddch('_');
13856 i++;
13857 } else {
13858 lynx_start_underline();
13859 }
13860 break;
13861
13862 case LY_UNDERLINE_END_CHAR:
13863 if (dump_output_immediately && use_underscore) {
13864 LYaddch('_');
13865 i++;
13866 } else {
13867 lynx_stop_underline();
13868 }
13869 break;
13870
13871 case LY_BOLD_START_CHAR:
13872 lynx_start_bold();
13873 break;
13874
13875 case LY_BOLD_END_CHAR:
13876 lynx_stop_bold();
13877 break;
13878
13879 #endif
13880 case LY_SOFT_NEWLINE:
13881 if (!dump_output_immediately) {
13882 LYaddch('+');
13883 i++;
13884 }
13885 break;
13886
13887 case LY_SOFT_HYPHEN:
13888 if (*data != '\0' ||
13889 isspace(UCH(LastDisplayChar)) ||
13890 LastDisplayChar == '-') {
13891 /*
13892 * Ignore the soft hyphen if it is not the last character in
13893 * the line. Also ignore it if it first character following
13894 * the margin, or if it is preceded by a white character (we
13895 * loaded 'M' into LastDisplayChar if it was a multibyte
13896 * character) or hyphen, though it should have been excluded by
13897 * HText_appendCharacter() or by split_line() in those cases.
13898 * -FM
13899 */
13900 break;
13901 } else {
13902 /*
13903 * Make it a hard hyphen and fall through. -FM
13904 */
13905 buffer[0] = '-';
13906 }
13907 /* FALLTHRU */
13908
13909 default:
13910 if (text->T.output_utf8 && is8bits(buffer[0])) {
13911 utf_extra = utf8_length(text->T.output_utf8, data - 1);
13912 LastDisplayChar = 'M';
13913 }
13914 if (utf_extra) {
13915 LYStrNCpy(&buffer[1], data, utf_extra);
13916 LYaddstr(buffer);
13917 buffer[1] = '\0';
13918 data += utf_extra;
13919 utf_extra = 0;
13920 } else if (is_CJK2(buffer[0])) {
13921 /*
13922 * For CJK strings, by Masanobu Kimura.
13923 */
13924 if (i <= DISPLAY_COLS) {
13925 buffer[1] = *data;
13926 buffer[2] = '\0';
13927 data++;
13928 i++;
13929 LYaddstr(buffer);
13930 buffer[1] = '\0';
13931 /*
13932 * For now, load 'M' into LastDisplayChar, but we should
13933 * check whether it's white and if so, use ' '. I don't
13934 * know if there actually are white CJK characters, and
13935 * we're loading ' ' for multibyte spacing characters in
13936 * this code set, but this will become an issue when the
13937 * development code set's multibyte character handling is
13938 * used. -FM
13939 */
13940 LastDisplayChar = 'M';
13941 }
13942 } else {
13943 LYaddstr(buffer);
13944 LastDisplayChar = buffer[0];
13945 }
13946 if (DidWrap(YP, XP))
13947 break;
13948 i++;
13949 } /* end of switch */
13950 } /* end of while */
13951
13952 #ifndef USE_COLOR_STYLE
13953 lynx_stop_underline();
13954 lynx_stop_bold();
13955 #else
13956
13957 while (current_style < line->numstyles) {
13958 LynxChangeStyle(CStyle.sc_style, CStyle.sc_direction);
13959 current_style++;
13960 }
13961
13962 #undef CStyle
13963 #endif
13964 return;
13965 }
13966 #endif /* USE_COLOR_STYLE */
13967
13968 #ifndef USE_COLOR_STYLE
13969 /*
13970 * Function move_to_glyph is called from LYMoveToLink and does all
13971 * the real work for it.
13972 * The pair LYMoveToLink()/move_to_glyph() is similar to the pair
13973 * redraw_lines_of_link()/redraw_part_of_line(), some key differences:
13974 * LYMoveToLink/move_to_glyph redraw_*
13975 * -----------------------------------------------------------------
13976 * - used without color style - used with color style
13977 * - handles showing WHEREIS target - WHEREIS handled elsewhere
13978 * - handles only one line - handles first two lines for
13979 * hypertext anchors
13980 * - right columns position for UTF-8
13981 * by redrawing as necessary
13982 * - currently used for highlight - currently used for highlight
13983 * ON and OFF OFF
13984 *
13985 * Eventually the two sets of function should be unified, and should handle
13986 * UTF-8 positioning, both lines of hypertext anchors, and WHEREIS in all
13987 * cases. If possible. The complex WHEREIS target logic in LYhighlight()
13988 * could then be completely removed. - kw
13989 */
move_to_glyph(int YP,int XP,int XP_draw_min,const char * data,int datasize,unsigned offset,const char * target,const char * hightext,int flags,int utf_flag)13990 static void move_to_glyph(int YP,
13991 int XP,
13992 int XP_draw_min,
13993 const char *data,
13994 int datasize,
13995 unsigned offset,
13996 const char *target,
13997 const char *hightext,
13998 int flags,
13999 int utf_flag)
14000 {
14001 char buffer[7];
14002 const char *end_of_data;
14003 size_t utf_extra = 0;
14004
14005 #if defined(SHOW_WHEREIS_TARGETS)
14006 const char *cp_tgt;
14007 int i_start_tgt = 0, i_after_tgt;
14008 int HitOffset, LenNeeded;
14009 #endif /* SHOW_WHEREIS_TARGETS */
14010 BOOL intarget = NO;
14011 BOOL inunderline = NO;
14012 BOOL inbold = NO;
14013 BOOL drawing = NO;
14014 BOOL inU = NO;
14015 BOOL hadutf8 = NO;
14016 BOOL incurlink = NO;
14017 BOOL drawingtarget = NO;
14018 BOOL flag = NO;
14019 const char *sdata = data;
14020 char LastDisplayChar = ' ';
14021
14022 int i = (int) offset; /* FIXME: should be columns, not offset? */
14023 int last_i = DISPLAY_COLS;
14024 int XP_link = XP; /* column of link */
14025 int XP_next = XP; /* column to move to when done drawing */
14026 int linkvlen;
14027
14028 int len;
14029
14030 if (no_title)
14031 YP -= TITLE_LINES;
14032
14033 if (flags & 1)
14034 flag = YES;
14035 if (flags & 2)
14036 inU = YES;
14037 /* Set up the multibyte character buffer */
14038 buffer[0] = buffer[1] = buffer[2] = '\0';
14039 /*
14040 * Add offset, making sure that we do not
14041 * go over the COLS limit on the display.
14042 */
14043 if (hightext != 0) {
14044 #ifdef WIDEC_CURSES
14045 last_i = i + LYstrExtent2(data, datasize);
14046 #endif
14047 linkvlen = LYmbcsstrlen(hightext, utf_flag, YES);
14048 } else {
14049 linkvlen = 0;
14050 }
14051 if (i >= last_i)
14052 i = last_i - 1;
14053
14054 /*
14055 * Scan through the data, making sure that we do not
14056 * go over the COLS limit on the display etc.
14057 */
14058 len = datasize;
14059 end_of_data = data + len;
14060
14061 #if defined(SHOW_WHEREIS_TARGETS)
14062 /*
14063 * If the target overlaps with the part of this line that
14064 * we are drawing, it will be emphasized.
14065 */
14066 i_after_tgt = i;
14067 if (target) {
14068 cp_tgt = LYno_attr_mb_strstr(sdata,
14069 target,
14070 utf_flag, YES,
14071 &HitOffset,
14072 &LenNeeded);
14073 if (cp_tgt) {
14074 if ((int) offset + LenNeeded > last_i ||
14075 ((int) offset + HitOffset >= XP + linkvlen)) {
14076 cp_tgt = NULL;
14077 } else {
14078 i_start_tgt = i + HitOffset;
14079 i_after_tgt = i + LenNeeded;
14080 }
14081 }
14082 } else {
14083 cp_tgt = NULL;
14084 }
14085 #endif /* SHOW_WHEREIS_TARGETS */
14086
14087 /*
14088 * Iterate through the line data from the start, keeping track of
14089 * the display ("glyph") position in i. Drawing will be turned
14090 * on when either the first UTF-8 sequence (that occurs after
14091 * XP_draw_min) is found, or when we reach the link itself (if
14092 * highlight is non-NULL). - kw
14093 */
14094 while ((i <= last_i) && data < end_of_data && (*data != '\0')) {
14095
14096 if (hightext && i >= XP && !incurlink) {
14097
14098 /*
14099 * We reached the position of link itself, and hightext is
14100 * non-NULL. We switch data from being a pointer into the HTLine
14101 * to being a pointer into hightext. Normally (as long as this
14102 * routine is applied to normal hyperlink anchors) the text in
14103 * hightext will be identical to that part of the HTLine that
14104 * data was already pointing to, except that special attribute
14105 * chars LY_BOLD_START_CHAR etc., have been stripped out (see
14106 * HText_trimHightext). So the switching should not result in
14107 * any different display, but it ensures that it doesn't go
14108 * unnoticed if somehow hightext got messed up somewhere else.
14109 * This is also useful in preparation for using this function
14110 * for something else than normal hyperlink anchors, i.e., form
14111 * fields.
14112 * Turn on drawing here or make sure it gets turned on before the
14113 * next actual normal character is handled. - kw
14114 */
14115 data = hightext;
14116 len = (int) strlen(hightext);
14117 end_of_data = hightext + len;
14118 last_i = i + len;
14119 XP_next += linkvlen;
14120 incurlink = YES;
14121 #ifdef SHOW_WHEREIS_TARGETS
14122 if (cp_tgt) {
14123 if (flag && i_after_tgt >= XP)
14124 i_after_tgt = XP - 1;
14125 }
14126 #endif
14127 /*
14128 * The logic of where to set in-target drawing target etc.
14129 * and when to react to it should be cleaned up (here and
14130 * further below). For now this seems to work but isn't
14131 * very clear. The complications arise from reproducing
14132 * the behavior (previously done in LYhighlight()) for target
14133 * strings that fall into or overlap a link: use target
14134 * emphasis for the target string, except for the first
14135 * and last character of the anchor text if the anchor is
14136 * highlighted as "current link". - kw
14137 */
14138 if (!drawing) {
14139 #ifdef SHOW_WHEREIS_TARGETS
14140 if (intarget) {
14141 if (i_after_tgt > i) {
14142 LYmove(YP, i);
14143 if (flag) {
14144 drawing = YES;
14145 drawingtarget = NO;
14146 if (inunderline)
14147 inU = YES;
14148 lynx_start_link_color(flag, inU);
14149 } else {
14150 drawing = YES;
14151 drawingtarget = YES;
14152 LYstartTargetEmphasis();
14153 }
14154 }
14155 }
14156 #endif /* SHOW_WHEREIS_TARGETS */
14157 } else {
14158 #ifdef SHOW_WHEREIS_TARGETS
14159 if (intarget && i_after_tgt > i) {
14160 if (flag && (data == hightext)) {
14161 drawingtarget = NO;
14162 LYstopTargetEmphasis();
14163 }
14164 } else if (!intarget)
14165 #endif /* SHOW_WHEREIS_TARGETS */
14166 {
14167 if (inunderline)
14168 inU = YES;
14169 if (inunderline)
14170 lynx_stop_underline();
14171 if (inbold)
14172 lynx_stop_bold();
14173 lynx_start_link_color(flag, inU);
14174 }
14175
14176 }
14177 }
14178 if (i >= last_i || data >= end_of_data)
14179 break;
14180 if ((buffer[0] = *data) == '\0')
14181 break;
14182 #if defined(SHOW_WHEREIS_TARGETS)
14183 /*
14184 * Look for a subsequent occurrence of the target string,
14185 * if we had a previous one and have now stepped past it. - kw
14186 */
14187 if (cp_tgt && i >= i_after_tgt) {
14188 if (intarget) {
14189
14190 if (incurlink && flag && i == last_i - 1)
14191 cp_tgt = NULL;
14192 else
14193 cp_tgt = LYno_attr_mb_strstr(sdata,
14194 target,
14195 utf_flag, YES,
14196 &HitOffset,
14197 &LenNeeded);
14198 if (cp_tgt) {
14199 i_start_tgt = i + HitOffset;
14200 i_after_tgt = i + LenNeeded;
14201 if (incurlink) {
14202 if (flag && i_start_tgt == XP_link)
14203 i_start_tgt++;
14204 if (flag && i_start_tgt == last_i - 1)
14205 i_start_tgt++;
14206 if (flag && i_after_tgt >= last_i)
14207 i_after_tgt = last_i - 1;
14208 if (flag && i_start_tgt >= last_i)
14209 cp_tgt = NULL;
14210 } else if (i_start_tgt == last_i) {
14211 if (flag)
14212 i_start_tgt++;
14213 }
14214 }
14215 if (!cp_tgt || i_start_tgt != i) {
14216 intarget = NO;
14217 if (drawing) {
14218 if (drawingtarget) {
14219 drawingtarget = NO;
14220 LYstopTargetEmphasis();
14221 if (incurlink) {
14222 lynx_start_link_color(flag, inU);
14223 }
14224 }
14225 if (!incurlink) {
14226 if (inbold)
14227 lynx_start_bold();
14228 if (inunderline)
14229 lynx_start_underline();
14230 }
14231 }
14232 }
14233 }
14234 }
14235 #endif /* SHOW_WHEREIS_TARGETS */
14236
14237 /*
14238 * Advance data to point to the next input char (for the
14239 * next round). Advance sdata, used for searching for a
14240 * target string, so that they stay in synch. As long
14241 * as we are not within the highlight text, data and sdata
14242 * have identical values. After we have switched data to
14243 * point into hightext, sdata remains a pointer into the
14244 * HTLine (so that we don't miss a partial target match at
14245 * the end of the anchor text). So sdata has to sometimes
14246 * skip additional special attribute characters that are
14247 * not present in highlight in order to stay in synch. - kw
14248 */
14249 data++;
14250 if (incurlink) {
14251 while (IsNormalChar(*sdata)) {
14252 ++sdata;
14253 }
14254 }
14255
14256 switch (buffer[0]) {
14257
14258 case LY_UNDERLINE_START_CHAR:
14259 if (!drawing || !incurlink)
14260 inunderline = YES;
14261 if (drawing && !intarget && !incurlink)
14262 lynx_start_underline();
14263 break;
14264
14265 case LY_UNDERLINE_END_CHAR:
14266 inunderline = NO;
14267 if (drawing && !intarget && !incurlink)
14268 lynx_stop_underline();
14269 break;
14270
14271 case LY_BOLD_START_CHAR:
14272 if (!drawing || !incurlink)
14273 inbold = YES;
14274 if (drawing && !intarget && !incurlink)
14275 lynx_start_bold();
14276 break;
14277
14278 case LY_BOLD_END_CHAR:
14279 inbold = NO;
14280 if (drawing && !intarget && !incurlink)
14281 lynx_stop_bold();
14282 break;
14283
14284 case LY_SOFT_NEWLINE:
14285 if (drawing) {
14286 LYaddch('+');
14287 }
14288 i++;
14289 break;
14290
14291 case LY_SOFT_HYPHEN:
14292 if (*data != '\0' ||
14293 isspace(UCH(LastDisplayChar)) ||
14294 LastDisplayChar == '-') {
14295 /*
14296 * Ignore the soft hyphen if it is not the last
14297 * character in the line. Also ignore it if it
14298 * first character following the margin, or if it
14299 * is preceded by a white character (we loaded 'M'
14300 * into LastDisplayChar if it was a multibyte
14301 * character) or hyphen, though it should have
14302 * been excluded by HText_appendCharacter() or by
14303 * split_line() in those cases. -FM
14304 */
14305 break;
14306 } else {
14307 /*
14308 * Make it a hard hyphen and fall through. -FM
14309 */
14310 buffer[0] = '-';
14311 }
14312 /* FALLTHRU */
14313
14314 default:
14315 /*
14316 * We have got an actual normal displayable character, or
14317 * the start of one. Before proceeding check whether
14318 * drawing needs to be turned on now. - kw
14319 */
14320 #if defined(SHOW_WHEREIS_TARGETS)
14321 if (incurlink && intarget && flag && i_after_tgt > i) {
14322 if (i == last_i - 1) {
14323 i_after_tgt = i;
14324 } else if (i == last_i - 2 && IS_CJK_TTY &&
14325 is8bits(buffer[0])) {
14326 i_after_tgt = i;
14327 cp_tgt = NULL;
14328 if (drawing) {
14329 if (drawingtarget) {
14330 LYstopTargetEmphasis();
14331 drawingtarget = NO;
14332 lynx_start_link_color(flag, inU);
14333 }
14334 }
14335 }
14336 }
14337 if (cp_tgt && i >= i_start_tgt && sdata > cp_tgt) {
14338 if (!intarget ||
14339 (intarget && incurlink && !drawingtarget)) {
14340
14341 if (incurlink && drawing &&
14342 !(flag &&
14343 (i == XP_link || i == last_i - 1))) {
14344 lynx_stop_link_color(flag, inU);
14345 }
14346 if (incurlink && !drawing) {
14347 LYmove(YP, i);
14348 if (inunderline)
14349 inU = YES;
14350 if (flag && (i == XP_link || i == last_i - 1)) {
14351 lynx_start_link_color(flag, inU);
14352 drawingtarget = NO;
14353 } else {
14354 LYstartTargetEmphasis();
14355 drawingtarget = YES;
14356 }
14357 drawing = YES;
14358 } else if (incurlink && drawing &&
14359 intarget && !drawingtarget &&
14360 (flag &&
14361 (i == XP_link))) {
14362 if (inunderline)
14363 inU = YES;
14364 lynx_start_link_color(flag, inU);
14365 } else if (drawing &&
14366 !(flag &&
14367 (i == XP_link || (incurlink && i == last_i - 1)))) {
14368 LYstartTargetEmphasis();
14369 drawingtarget = YES;
14370 }
14371 intarget = YES;
14372 }
14373 } else
14374 #endif /* SHOW_WHEREIS_TARGETS */
14375 if (incurlink) {
14376 if (!drawing) {
14377 LYmove(YP, i);
14378 if (inunderline)
14379 inU = YES;
14380 lynx_start_link_color(flag, inU);
14381 drawing = YES;
14382 }
14383 }
14384
14385 i++;
14386 #ifndef WIDEC_CURSES
14387 if (utf_flag && is8bits(buffer[0])) {
14388 hadutf8 = YES;
14389 utf_extra = utf8_length(utf_flag, data - 1);
14390 LastDisplayChar = 'M';
14391 }
14392 #endif
14393 if (utf_extra) {
14394 LYStrNCpy(&buffer[1], data, utf_extra);
14395 if (!drawing && i >= XP_draw_min) {
14396 LYmove(YP, i - 1);
14397 drawing = YES;
14398 #if defined(SHOW_WHEREIS_TARGETS)
14399 if (intarget) {
14400 drawingtarget = YES;
14401 LYstartTargetEmphasis();
14402 } else
14403 #endif /* SHOW_WHEREIS_TARGETS */
14404 {
14405 if (inbold)
14406 lynx_start_bold();
14407 if (inunderline)
14408 lynx_start_underline();
14409 }
14410 }
14411 LYaddstr(buffer);
14412 buffer[1] = '\0';
14413 sdata += utf_extra;
14414 data += utf_extra;
14415 utf_extra = 0;
14416 } else if (IS_CJK_TTY && is8bits(buffer[0])
14417 && (!conv_jisx0201kana && (kanji_code != SJIS))) {
14418 /*
14419 * For CJK strings, by Masanobu Kimura.
14420 */
14421 if (drawing && (i <= last_i)) {
14422 buffer[1] = *data;
14423 LYaddstr(buffer);
14424 buffer[1] = '\0';
14425 }
14426 i++;
14427 sdata++;
14428 data++;
14429 /*
14430 * For now, load 'M' into LastDisplayChar, but we should
14431 * check whether it's white and if so, use ' '. I don't
14432 * know if there actually are white CJK characters, and
14433 * we're loading ' ' for multibyte spacing characters in
14434 * this code set, but this will become an issue when the
14435 * development code set's multibyte character handling is
14436 * used. -FM
14437 */
14438 LastDisplayChar = 'M';
14439 } else {
14440 if (drawing) {
14441 LYaddstr(buffer);
14442 }
14443 LastDisplayChar = buffer[0];
14444 }
14445 } /* end of switch */
14446 } /* end of while */
14447
14448 if (!drawing) {
14449 LYmove(YP, XP_next);
14450 lynx_start_link_color(flag, inU);
14451 } else {
14452 #if defined(SHOW_WHEREIS_TARGETS)
14453 if (drawingtarget) {
14454 LYstopTargetEmphasis();
14455 lynx_start_link_color(flag, inU);
14456 }
14457 #endif /* SHOW_WHEREIS_TARGETS */
14458 if (hadutf8) {
14459 LYtouchline(YP);
14460 }
14461 }
14462 return;
14463 }
14464 #endif /* !USE_COLOR_STYLE */
14465
14466 #ifndef USE_COLOR_STYLE
14467 /*
14468 * Move cursor position to a link's place in the display.
14469 * The "moving to" is done by scanning through the line's
14470 * character data in the corresponding HTLine of HTMainText,
14471 * and starting to draw when a UTF-8 encoded non-ASCII character
14472 * is encountered before the link (with some protection against
14473 * overwriting form fields). This refreshing of preceding data is
14474 * necessary for preventing curses's or slang's display logic from
14475 * getting too clever; their logic counts character positions wrong
14476 * since they don't know about multi-byte characters that take up
14477 * only one screen position. So we have to make them forget their
14478 * idea of what's in a screen line drawn previously.
14479 * If hightext is non-NULL, it should be the anchor text for a normal
14480 * link as stored in a links[] element, and the anchor text will be
14481 * drawn too, with appropriate attributes. - kw
14482 */
LYMoveToLink(int cur,const char * target,const char * hightext,int flag,int inU,int utf_flag)14483 void LYMoveToLink(int cur,
14484 const char *target,
14485 const char *hightext,
14486 int flag,
14487 int inU,
14488 int utf_flag)
14489 {
14490 #define pvtTITLE_HEIGHT 1
14491 HTLine *todr;
14492 int i, n = 0;
14493 int XP_draw_min = 0;
14494 int flags = ((flag == TRUE) ? 1 : 0) | (inU ? 2 : 0);
14495
14496 /*
14497 * We need to protect changed form text fields preceding this
14498 * link on the same line against overwriting. - kw
14499 */
14500 for (i = cur - 1; i >= 0; i++) {
14501 if (links[i].ly < links[cur].ly)
14502 break;
14503 if (links[i].type == WWW_FORM_LINK_TYPE) {
14504 XP_draw_min = links[i].ly + links[i].l_form->size;
14505 break;
14506 }
14507 }
14508
14509 /* Find the right HTLine. */
14510 if (!HTMainText) {
14511 todr = NULL;
14512 } else if (HTMainText->stale) {
14513 todr = FirstHTLine(HTMainText);
14514 n = links[cur].ly - pvtTITLE_HEIGHT + HTMainText->top_of_screen;
14515 } else {
14516 todr = HTMainText->top_of_screen_line;
14517 n = links[cur].ly - pvtTITLE_HEIGHT;
14518 }
14519 for (i = 0; i < n && todr; i++) {
14520 todr = (todr == HTMainText->last_line) ? NULL : todr->next;
14521 }
14522 if (todr) {
14523 if (target && *target == '\0')
14524 target = NULL;
14525 move_to_glyph(links[cur].ly, links[cur].lx, XP_draw_min,
14526 todr->data, todr->size, todr->offset,
14527 target, hightext, flags, utf_flag);
14528 } else {
14529 /* This should not happen. */
14530 move_to_glyph(links[cur].ly, links[cur].lx, XP_draw_min,
14531 "", 0, (unsigned) links[cur].lx,
14532 target, hightext, flags, utf_flag);
14533 }
14534 }
14535 #endif /* !USE_COLOR_STYLE */
14536
14537 /*
14538 * This is used only if compiled with lss support. It's called to redraw a
14539 * regular link when it's being unhighlighted in LYhighlight().
14540 */
14541 #ifdef USE_COLOR_STYLE
redraw_lines_of_link(int cur)14542 void redraw_lines_of_link(int cur)
14543 {
14544 #define pvtTITLE_HEIGHT 1
14545 HTLine *todr1;
14546 int lines_back;
14547 int row, col, count;
14548 const char *text;
14549
14550 if (HTMainText->next_line == HTMainText->last_line) {
14551 /* we are at the last page - that is partially filled */
14552 lines_back = HTMainText->Lines - (links[cur].ly - pvtTITLE_HEIGHT +
14553 HTMainText->top_of_screen);
14554 } else {
14555 lines_back = display_lines - (links[cur].ly - pvtTITLE_HEIGHT);
14556 }
14557 todr1 = HTMainText->next_line;
14558 while (lines_back-- > 0)
14559 todr1 = todr1->prev;
14560
14561 row = links[cur].ly;
14562 if (no_title)
14563 row -= TITLE_LINES;
14564
14565 for (count = 0;
14566 row <= display_lines && (text = LYGetHiliteStr(cur, count)) != NULL;
14567 ++count) {
14568 col = LYGetHilitePos(cur, count);
14569 if (col >= 0) {
14570 LYmove(row, col);
14571 redraw_part_of_line(todr1, text, (int) strlen(text), HTMainText);
14572 }
14573 todr1 = todr1->next;
14574 row++;
14575 }
14576 #undef pvtTITLE_HEIGHT
14577 return;
14578 }
14579 #endif
14580
14581 #ifdef USE_PRETTYSRC
HTMark_asSource(void)14582 void HTMark_asSource(void)
14583 {
14584 if (HTMainText)
14585 HTMainText->source = TRUE;
14586 }
14587 #endif
14588
HText_getKcode(HText * text)14589 HTkcode HText_getKcode(HText *text)
14590 {
14591 return text->kcode;
14592 }
14593
HText_updateKcode(HText * text,HTkcode kcode)14594 void HText_updateKcode(HText *text, HTkcode kcode)
14595 {
14596 text->kcode = kcode;
14597 }
14598
HText_getSpecifiedKcode(HText * text)14599 HTkcode HText_getSpecifiedKcode(HText *text)
14600 {
14601 return text->specified_kcode;
14602 }
14603
HText_updateSpecifiedKcode(HText * text,HTkcode kcode)14604 void HText_updateSpecifiedKcode(HText *text, HTkcode kcode)
14605 {
14606 text->specified_kcode = kcode;
14607 }
14608
HTMainText_Get_UCLYhndl(void)14609 int HTMainText_Get_UCLYhndl(void)
14610 {
14611 return (HTMainText ?
14612 HTAnchor_getUCLYhndl(HTMainText->node_anchor, UCT_STAGE_MIME)
14613 : -1);
14614 }
14615
14616 #ifdef USE_CACHEJAR
LYHandleCache(const char * arg,HTParentAnchor * anAnchor,HTFormat format_out,HTStream * sink)14617 static int LYHandleCache(const char *arg,
14618 HTParentAnchor *anAnchor,
14619 HTFormat format_out,
14620 HTStream *sink)
14621 {
14622 HTFormat format_in = WWW_HTML;
14623 HTStream *target = NULL;
14624 char c;
14625 char *buf = NULL;
14626 char *title = NULL;
14627 char *address = NULL;
14628 char *content_type = NULL;
14629 char *content_language = NULL;
14630 char *content_encoding = NULL;
14631 char *content_location = NULL;
14632 char *content_disposition = NULL;
14633 char *content_md5 = NULL;
14634 char *message_id = NULL;
14635 char *date = NULL;
14636 char *owner = NULL;
14637 char *subject = NULL;
14638 char *expires = NULL;
14639 char *ETag = NULL;
14640 char *server = NULL;
14641 char *FileCache = NULL;
14642 char *last_modified = NULL;
14643 char *cache_control = NULL;
14644
14645 #ifdef USE_SOURCE_CACHE
14646 char *source_cache_file = NULL;
14647 #endif
14648 off_t Size = 0;
14649 int x = -1;
14650
14651 /*
14652 * Check if there is something to do.
14653 */
14654 if (HTList_count(loaded_texts) == 0) {
14655 HTProgress(CACHE_JAR_IS_EMPTY);
14656 LYSleepMsg();
14657 HTNoDataOK = 1;
14658 return (HT_NO_DATA);
14659 }
14660
14661 /*
14662 * If # of LYNXCACHE:/# is number ask user if he/she want to delete it.
14663 */
14664 if (sscanf(arg, STR_LYNXCACHE "/%d", &x) == 1 && x > 0) {
14665 CTRACE((tfp, "LYNXCACHE number is %d\n", x));
14666 _statusline(CACHE_D_OR_CANCEL);
14667 c = (char) LYgetch_single();
14668 if (c == 'D') {
14669 HText *t = (HText *) HTList_objectAt(loaded_texts, x - 1);
14670
14671 HTList_removeObjectAt(loaded_texts, x - 1);
14672 HText_free(t);
14673 }
14674 return (HT_NO_DATA);
14675 }
14676
14677 /*
14678 * If we get to here, it was a LYNXCACHE:/ URL for creating and displaying
14679 * the Cache Jar Page.
14680 * Set up an HTML stream and return an updated Cache Jar Page.
14681 */
14682 target = HTStreamStack(format_in,
14683 format_out,
14684 sink, anAnchor);
14685 if (target == NULL) {
14686 HTSprintf0(&buf, CANNOT_CONVERT_I_TO_O,
14687 HTAtom_name(format_in), HTAtom_name(format_out));
14688 HTAlert(buf);
14689 FREE(buf);
14690 return (HT_NOT_LOADED);
14691 }
14692
14693 /*
14694 * Load HTML strings into buf and pass buf to the target for parsing and
14695 * rendering.
14696 */
14697 #define PUTS(buf) (*target->isa->put_block)(target, buf, (int) strlen(buf))
14698
14699 HTSprintf0(&buf,
14700 "<html>\n<head>\n<title>%s</title>\n</head>\n<body>\n",
14701 CACHE_JAR_TITLE);
14702 PUTS(buf);
14703 HTSprintf0(&buf, "<h1>%s (%s)%s<a href=\"%s%s\">%s</a></h1>\n",
14704 LYNX_NAME, LYNX_VERSION,
14705 HELP_ON_SEGMENT,
14706 helpfilepath, CACHE_JAR_HELP, CACHE_JAR_TITLE);
14707 PUTS(buf);
14708
14709 /*
14710 * Max number of cached documents is always same as HTCacheSize.
14711 * We count them from oldest to newest. Currently cached document
14712 * is *never* listed, resulting in maximal entries of Cache Jar
14713 * to be HTCacheSize - 1
14714 */
14715 for (x = HTList_count(loaded_texts) - 1; x > 0; x--) {
14716 /*
14717 * The number of the document in the cache list, its title in a link,
14718 * and its address and memory allocated for each cached document.
14719 */
14720 HText *cachedoc = (HText *) HTList_objectAt(loaded_texts, x);
14721
14722 if (cachedoc != 0) {
14723 HTParentAnchor *docanchor = cachedoc->node_anchor;
14724
14725 if (docanchor != 0) {
14726 #ifdef USE_SOURCE_CACHE
14727 source_cache_file = docanchor->source_cache_file;
14728 #endif
14729 Size = docanchor->content_length;
14730 StrAllocCopy(title, docanchor->title);
14731 StrAllocCopy(address, docanchor->address);
14732 content_type = docanchor->content_type;
14733 content_language = docanchor->content_language;
14734 content_encoding = docanchor->content_encoding;
14735 content_location = docanchor->content_location;
14736 content_disposition = docanchor->content_disposition;
14737 content_md5 = docanchor->content_md5;
14738 message_id = docanchor->message_id;
14739 owner = docanchor->owner;
14740 StrAllocCopy(subject, docanchor->subject);
14741 date = docanchor->date;
14742 expires = docanchor->expires;
14743 ETag = docanchor->ETag;
14744 StrAllocCopy(server, docanchor->server);
14745 FileCache = docanchor->FileCache;
14746 last_modified = docanchor->last_modified;
14747 cache_control = docanchor->cache_control;
14748 }
14749 }
14750
14751 LYEntify(&address, TRUE);
14752 if (isEmpty(title))
14753 StrAllocCopy(title, NO_TITLE);
14754 else
14755 LYEntify(&title, TRUE);
14756
14757 HTSprintf0(&buf,
14758 "<p><em>%d.</em> Title: <a href=\"%s%d\">%s</a><br />URL: <a href=\"%s\">%s</a><br />",
14759 x, STR_LYNXCACHE, x, title, address, address);
14760 PUTS(buf);
14761 if (Size > 0) {
14762 HTSprintf0(&buf, "Size: %" PRI_off_t " ", CAST_off_t (Size));
14763
14764 PUTS(buf);
14765 }
14766 if (cachedoc != NULL && cachedoc->Lines > 0) {
14767 HTSprintf0(&buf, "Lines: %d ", cachedoc->Lines);
14768 PUTS(buf);
14769 }
14770 if (FileCache != NULL) {
14771 HTSprintf0(&buf, "File-Cache: <a href=\"file://%s\">%s</a> ",
14772 FileCache, FileCache);
14773 PUTS(buf);
14774 }
14775 if (cache_control != NULL) {
14776 HTSprintf0(&buf, "Cache-Control: %s ", cache_control);
14777 PUTS(buf);
14778 }
14779 if (content_type != NULL) {
14780 HTSprintf0(&buf, "Content-Type: %s ", content_type);
14781 PUTS(buf);
14782 }
14783 if (content_language != NULL) {
14784 HTSprintf0(&buf, "Content-Language: %s ", content_language);
14785 PUTS(buf);
14786 }
14787 if (content_encoding != NULL) {
14788 HTSprintf0(&buf, "Content-Encoding: %s ", content_encoding);
14789 PUTS(buf);
14790 }
14791 if (content_location != NULL) {
14792 HTSprintf0(&buf, "Content-Location: %s ", content_location);
14793 PUTS(buf);
14794 }
14795 if (content_disposition != NULL) {
14796 HTSprintf0(&buf, "Content-Disposition: %s ", content_disposition);
14797 PUTS(buf);
14798 }
14799 if (content_md5 != NULL) {
14800 HTSprintf0(&buf, "Content-MD5: %s ", content_md5);
14801 PUTS(buf);
14802 }
14803 if (message_id != NULL) {
14804 HTSprintf0(&buf, "Message-ID: %s ", message_id);
14805 PUTS(buf);
14806 }
14807 if (subject != NULL) {
14808 LYEntify(&subject, TRUE);
14809 HTSprintf0(&buf, "Subject: %s ", subject);
14810 PUTS(buf);
14811 }
14812 if (owner != NULL) {
14813 HTSprintf0(&buf, "Owner: <a href=%s>%s</a> ", owner, owner);
14814 PUTS(buf);
14815 }
14816 if (date != NULL) {
14817 HTSprintf0(&buf, "Date: %s ", date);
14818 PUTS(buf);
14819 }
14820 if (expires != NULL) {
14821 HTSprintf0(&buf, "Expires: %s ", expires);
14822 PUTS(buf);
14823 }
14824 if (last_modified != NULL) {
14825 HTSprintf0(&buf, "Last-Modified: %s ", last_modified);
14826 PUTS(buf);
14827 }
14828 if (ETag != NULL) {
14829 HTSprintf0(&buf, "ETag: %s ", ETag);
14830 PUTS(buf);
14831 }
14832 if (server != NULL) {
14833 LYEntify(&server, TRUE);
14834 HTSprintf0(&buf, "Server: <em>%s</em> ", server);
14835 PUTS(buf);
14836 }
14837 #ifdef USE_SOURCE_CACHE
14838 if (source_cache_file != NULL) {
14839 HTSprintf0(&buf,
14840 "Source-Cache-File: <a href=\"file://%s\">%s</a>",
14841 source_cache_file, source_cache_file);
14842 PUTS(buf);
14843 }
14844 #endif
14845 HTSprintf0(&buf, "<br />");
14846 PUTS(buf);
14847 }
14848 HTSprintf0(&buf, "</body></html>");
14849 PUTS(buf);
14850 FREE(subject);
14851 FREE(title);
14852 FREE(address);
14853 FREE(server);
14854
14855 /*
14856 * Free the target to complete loading of the Cache Jar Page, and report a
14857 * successful load.
14858 */
14859 (*target->isa->_free) (target);
14860 FREE(buf);
14861 return (HT_LOADED);
14862 }
14863
14864 #ifdef GLOBALDEF_IS_MACRO
14865 #define _LYCACHE_C_GLOBALDEF_1_INIT { "LYNXCACHE",LYHandleCache,0}
14866 GLOBALDEF(HTProtocol, LYLynxCache, _LYCACHE_C_GLOBALDEF_1_INIT);
14867 #else
14868 GLOBALDEF HTProtocol LYLynxCache =
14869 {"LYNXCACHE", LYHandleCache, 0};
14870 #endif /* GLOBALDEF_IS_MACRO */
14871 #endif /* USE_CACHEJAR */
14872