1 /***************************************************************************
2  *  Pinfo is a ncurses based lynx style info documentation browser
3  *
4  *  Copyright (C) 1999  Przemek Borys <pborys@dione.ids.pl>
5  *  Copyright (C) 2005  Bas Zoetekouw <bas@debian.org>
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of version 2 of the GNU General Public License as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful, but
12  *  WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
19  *  USA
20  ***************************************************************************/
21 #include "common_includes.h"
22 
23 #define MENU_DOT 0
24 #define NOTE_DOT 1
25 
26 int
compare_hyperlink(const void * a,const void * b)27 compare_hyperlink(const void *a, const void *b)
28 {
29 	return ((HyperObject *) a)->col -((HyperObject *) b)->col;
30 }
31 
32 void
sort_hyperlinks_from_current_line(long startlink,long endlink)33 sort_hyperlinks_from_current_line(long startlink, long endlink)
34 {
35 	qsort(hyperobjects + startlink, endlink - startlink, sizeof(HyperObject), compare_hyperlink);
36 }
37 
38 /*
39  * Compares two strings, ignoring whitespaces(tabs, spaces)
40  */
41 int
compare_tag_table_string(char * base,char * compared)42 compare_tag_table_string(char *base, char *compared)
43 {
44 	int i, j;
45 
46 	j = 0;
47 
48 	for (i = 0; base[i] != 0; i++)
49 	{
50 		if (base[i] != compared[j])
51 		{
52 			if ((isspace(compared[j])) &&(isspace(base[i])));	/* OK--two blanks */
53 			else if (isspace(compared[j]))
54 				i--;		/* index of `base' should be unchanged after for's i++ */
55 			else if (isspace(base[i]))
56 				j--;		/* index of `compared' stands in place
57 							   and waits for base to skip blanks */
58 			else
59 				return (int) base[i] -(int) compared[j];
60 		}
61 		j++;
62 	}
63 	while (compared[j])		/* handle trailing whitespaces of variable `compared' */
64 	{
65 		if (!isspace(compared[j]))
66 			return (int) base[i] -(int) compared[j];
67 		j++;
68 	}
69 	return 0;
70 }
71 
72 /*
73  * checks if an item belongs to tag table. returns 1 on success and 0 on
74  * failure.  It should be optimised...
75  */
76 int
exists_in_tag_table(char * item)77 exists_in_tag_table(char *item)
78 {
79 	if (gettagtablepos(item) != -1)
80 		return 1;
81 	else
82 		return 0;
83 }
84 
85 
86 void
freelinks()87 freelinks()			/* frees space allocated previously by node-links */
88 {
89 	if ((hyperobjects)&&(hyperobjectcount))
90 		xfree(hyperobjects);
91 	hyperobjects = 0;
92 	hyperobjectcount = 0;
93 }
94 
95 /*
96  * Finds url end.  It is recognized by an invalid character.
97  */
98 /* TODO: fix possible string overflow (no bounds checking) */
99 char *
findurlend(char * str)100 findurlend(char *str)
101 {
102 	char *end;
103 	char *allowedchars = "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm1234567890-_/~.%=|:@";
104 	end = str;
105 	while (strchr(allowedchars, *end) != NULL)
106 		++end;
107 	if (end > str)
108 	{
109 		if (*(end - 1) == '.')
110 			end--;
111 	}
112 	assert(end>=str);
113 	return end;
114 }
115 
116 /*
117  * Searchs for a note/menu delimiter.  it may be dot, comma, tab, or newline.
118  */
119 /* TODO: fix possible string overflow (no bounds checking) */
120 char *
finddot(char * str,int mynote)121 finddot(char *str, int mynote)
122 {
123 	char *ptr = str;
124 	char *end[4] =
125 	{
126 		0, 0, 0, 0
127 	};
128 	char *closest = 0;
129 	int i;
130 	while (isspace(*ptr))	/* if there are only spaces and newline... */
131 	{
132 		if (*ptr == '\n')		/* then it's a `Menu:   \n' entry--skip it */
133 			return 0;
134 		ptr++;
135 	}
136 	end[0] = strchr(str, '.');	/* nodename entry may end with dot, comma */
137 	end[1] = strchr(str, ',');	/* tabulation, or newline */
138 	if (!mynote)
139 	{
140 		end[2] = strchr(str, '\t');
141 		end[3] = strchr(str, '\n');
142 	}
143 	else
144 		mynote = 2;
145 	if (end[0])
146 		closest = end[0];
147 	else if (end[1])
148 		closest = end[1];
149 	else if (end[2])
150 		closest = end[2];
151 	else if (end[3])
152 		closest = end[3];
153 	for (i = 1; i < mynote; i++)	/* find the delimiter, which was found most
154 								   recently */
155 	{
156 		if ((end[i] < closest) &&(end[i]))
157 			closest = end[i];
158 	}
159 	assert(closest>=str);
160 	return closest;
161 }
162 
163 /*
164  * Moves you to the beginning of username in email address.  If username has
165  * length=0, NULL is returned.
166  */
167 /* TODO: fix possible string overflow (no bounds checking) */
168 char *
findemailstart(char * str)169 findemailstart(char *str)
170 {
171 	char *allowedchars = "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm1234567890-_/~.%=|:";
172 	char *at = strchr(str, '@');
173 	if (at)
174 	{
175 		while (at > str)
176 		{
177 			at--;
178 			if (strchr(allowedchars, *at) == NULL)
179 			{
180 				if (*(at + 1) != '@')
181 					return at + 1;
182 				else
183 					return 0;
184 			}
185 		}
186 		if (*at != '@')
187 			return at;
188 		else
189 			return 0;
190 	}
191 	return 0;
192 }
193 
194 void
initializelinks(char * line1,char * line2,int line)195 initializelinks(char *line1, char *line2, int line)
196 {
197 	char *tmp;
198 	char *notestart = 0, *urlstart = 0, *urlend = 0;
199 	char *quotestart = 0, *quoteend = 0;
200 	char *buf = xmalloc(strlen(line1) + strlen(line2) + 1);
201 	/* required to sort properly the hyperlinks from current line only */
202 	unsigned long initialhyperobjectcount = hyperobjectcount;
203 	int changed;
204 	int line1len = strlen(line1);
205 
206 	strcpy(buf, line1);		/* copy two lines into one */
207 	if (strlen(line1))
208 		buf[strlen(line1) - 1] = ' ';	/* replace trailing '\n' with ' ' */
209 	strcat(buf, line2);
210 	/******************************************************************************
211 	 * First scan for some highlights ;) -- words enclosed with quotes             *
212 	 ******************************************************************************/
213 	quoteend = buf;
214 	do
215 	{
216 		changed = 0;
217 		/* find start of quoted text */
218 		if ( ((quotestart = strchr(quoteend, '`')) != NULL)
219 			 /* if it's in the first line of the two glued together */
220 			 && (quotestart < buf + line1len)
221 			 /* find the end of quoted text */
222 			 && ((quoteend = strchr(quotestart, '\'')) != NULL)
223 			 &&  (quoteend - quotestart > 1))
224 		{
225 			/* if this apostrophe is not a part of "haven't",
226 			 * "wouldn't", etc. */
227 			while (!strncmp(quoteend - 1, "n't", 3))
228 			{
229 				quoteend = strchr(quoteend + 1, '\'');
230 				if (!quoteend)
231 					break;
232 			}
233 			if (quoteend)
234 			{
235 				changed = 1;
236 				if (!hyperobjectcount)
237 					hyperobjects = xmalloc(sizeof(HyperObject));
238 				else
239 				{
240 					hyperobjects = xrealloc(hyperobjects,
241 							sizeof(HyperObject) *(hyperobjectcount + 1));
242 				}
243 				hyperobjects[hyperobjectcount].line = line;
244 				hyperobjects[hyperobjectcount].col =
245 					calculate_len(buf, quotestart + 1);
246 				hyperobjects[hyperobjectcount].breakpos = -1;	/* default */
247 				if (quoteend > buf + line1len)
248 				{
249 					hyperobjects[hyperobjectcount].breakpos =
250 						buf + line1len - quotestart - 1;
251 				}
252 				hyperobjects[hyperobjectcount].type = HIGHLIGHT;
253 				strncpy( hyperobjects[hyperobjectcount].node,
254 						quotestart + 1, quoteend - quotestart - 1);
255 				hyperobjects[hyperobjectcount].node[quoteend - quotestart - 1] = 0;
256 				strcpy(hyperobjects[hyperobjectcount].file, "");
257 				hyperobjects[hyperobjectcount].nodelen =
258 					strlen(hyperobjects[hyperobjectcount].node);
259 				hyperobjects[hyperobjectcount].filelen =
260 					strlen(hyperobjects[hyperobjectcount].file);
261 				hyperobjects[hyperobjectcount].tagtableoffset = -1;
262 				hyperobjectcount++;
263 			}
264 		}
265 	}
266 	while (changed);
267 	/******************************************************************************
268 	 * Look for e-mail url's                                                       *
269 	 ******************************************************************************/
270 	urlend = line1;
271 	do
272 	{
273 		changed = 0;
274 		if ((urlstart = findemailstart(urlend)) != NULL)
275 		{
276 			urlend = findurlend(urlstart);	/* always successful */
277 			if (!hyperobjectcount)
278 				hyperobjects = xmalloc(sizeof(HyperObject));
279 			else
280 			{
281 				hyperobjects = xrealloc(hyperobjects,
282 						sizeof(HyperObject) *(hyperobjectcount + 1));
283 			}
284 			hyperobjects[hyperobjectcount].line = line;
285 			hyperobjects[hyperobjectcount].col = calculate_len(line1, urlstart);
286 			hyperobjects[hyperobjectcount].breakpos = -1;
287 			hyperobjects[hyperobjectcount].type = 6;
288 			strncpy(hyperobjects[hyperobjectcount].node,
289 					urlstart, urlend - urlstart);
290 			hyperobjects[hyperobjectcount].node[urlend - urlstart] = 0;
291 			strcpy(hyperobjects[hyperobjectcount].file, "");
292 			hyperobjects[hyperobjectcount].nodelen =
293 				strlen(hyperobjects[hyperobjectcount].node);
294 			hyperobjects[hyperobjectcount].filelen =
295 				strlen(hyperobjects[hyperobjectcount].file);
296 			hyperobjects[hyperobjectcount].tagtableoffset = -1;
297 			if (strchr(hyperobjects[hyperobjectcount].node, '.') == NULL)
298 			{
299 				if (!hyperobjectcount)
300 					xfree(hyperobjects);
301 			}
302 			else
303 				hyperobjectcount++;
304 			changed = 1;
305 		}
306 
307 	}
308 	while (changed);
309 	/******************************************************************************
310 	 * First try to scan for menu. Use as many security mechanisms, as possible    *
311 	 ******************************************************************************/
312 
313 	if ((line1[0] == '*') &&(line1[1] == ' '))	/* menu */
314 	{
315 		/*************************************************************************
316 		 * Scan for normal menu of kind "*(infofile)reference:: comment",  where *
317 		 * the infofile parameter is optional, and indicates, that a reference   *
318 		 * matches different info file.                                          *
319 		 *************************************************************************/
320 		tmp = strstr(line1, "::");	/* "* menulink:: comment" */
321 		if (tmp != NULL)
322 		{
323 			if (!hyperobjectcount)
324 				hyperobjects = xmalloc(sizeof(HyperObject));
325 			else
326 			{
327 				hyperobjects = xrealloc(hyperobjects,
328 						sizeof(HyperObject) *(hyperobjectcount + 1));
329 			}
330 			if (line1[2] == '(')	/* if cross-info link */
331 			{
332 				char *end = strchr(line1, ')');
333 				/* if the ')' char was found, and was before '::' */
334 				if ((end != NULL) &&(end < tmp))
335 				{
336 					long FilenameLen =(long)(end - line1 - 3);
337 					long NodenameLen =(long)(tmp - end - 1);
338 					strncpy(hyperobjects[hyperobjectcount].file,
339 							line1 + 3, FilenameLen);
340 					hyperobjects[hyperobjectcount].file[FilenameLen] = 0;
341 					strncpy(hyperobjects[hyperobjectcount].node,
342 							end + 1, NodenameLen);
343 					hyperobjects[hyperobjectcount].node[NodenameLen] = 0;
344 					hyperobjects[hyperobjectcount].type = 0;
345 					hyperobjects[hyperobjectcount].line = line;
346 					hyperobjects[hyperobjectcount].col = 2;
347 					hyperobjects[hyperobjectcount].breakpos = -1;
348 					hyperobjects[hyperobjectcount].nodelen =
349 						strlen(hyperobjects[hyperobjectcount].node);
350 					hyperobjects[hyperobjectcount].filelen =
351 						strlen(hyperobjects[hyperobjectcount].file);
352 					hyperobjectcount++;
353 				}
354 			}
355 			else
356 			{
357 				/* if not cross-info link */
358 				long NodenameLen =(long)(tmp - line1 - 2);
359 				int goodHit = 0;
360 				strcpy(hyperobjects[hyperobjectcount].file, "");
361 				strncpy(hyperobjects[hyperobjectcount].node,
362 						line1 + 2, NodenameLen);
363 				hyperobjects[hyperobjectcount].node[NodenameLen] = 0;
364 				hyperobjects[hyperobjectcount].type = 0;
365 				hyperobjects[hyperobjectcount].line = line;
366 				hyperobjects[hyperobjectcount].col = 2;
367 				hyperobjects[hyperobjectcount].breakpos = -1;
368 				hyperobjects[hyperobjectcount].nodelen =
369 					strlen(hyperobjects[hyperobjectcount].node);
370 				hyperobjects[hyperobjectcount].filelen =
371 					strlen(hyperobjects[hyperobjectcount].file);
372 				if (exists_in_tag_table(hyperobjects[hyperobjectcount].node))
373 				{
374 					hyperobjectcount++;	/* yep, this was a good hit */
375 					goodHit = 1;
376 				}
377 				if (!goodHit && !hyperobjectcount)
378 				{
379 					xfree(hyperobjects);
380 					hyperobjects = 0;
381 				}
382 			}
383 		}
384 		/************************************************************
385 		 * Scan for menu references of form                         *
386 		 * "* Comment:[spaces](infofile)reference."                 *
387 		 ******************************************************************************/
388 		else if ((tmp = strrchr(line1, ':')) != NULL)
389 		{
390 			char *start = 0, *end = 0, *dot = 0;
391 			/* find the trailing dot */
392 			dot = finddot(tmp + 1, MENU_DOT);
393 			if (dot != NULL && (dot + 7 < dot + strlen(dot)) )
394 			{
395 				/* skip possible '.info' filename suffix when
396 				 * searching for ending dot */
397 				if (strncmp(dot, ".info)", 6) == 0)
398 					dot = finddot(dot + 1, MENU_DOT);
399 			}
400 			/* we make use of sequential AND evaluation: start must not be NULL! */
401 			if (((start = strchr(tmp, '(')) != NULL) &&(dot != NULL) &&
402 					((end = strchr(start, ')')) != NULL))
403 			{
404 				if ( (start < dot) && (end < dot) )	/* security mechanism ;) */
405 				{
406 					long FilenameLen =(long)(end - start - 1);
407 					long NodenameLen =(long)(dot - end - 1);
408 					if (!hyperobjectcount)
409 						hyperobjects = xmalloc(sizeof(HyperObject));
410 					else
411 					{
412 						hyperobjects = xrealloc(hyperobjects,
413 								sizeof(HyperObject) *(hyperobjectcount + 1));
414 					}
415 					strncpy(hyperobjects[hyperobjectcount].file,
416 							start + 1, FilenameLen);
417 					hyperobjects[hyperobjectcount].file[FilenameLen] = 0;
418 					strncpy(hyperobjects[hyperobjectcount].node,
419 							end + 1, NodenameLen);
420 					hyperobjects[hyperobjectcount].node[NodenameLen] = 0;
421 					hyperobjects[hyperobjectcount].type = 1;
422 					hyperobjects[hyperobjectcount].line = line;
423 					hyperobjects[hyperobjectcount].col =
424 						calculate_len(line1, start);
425 					hyperobjects[hyperobjectcount].breakpos = -1;
426 					hyperobjects[hyperobjectcount].nodelen =
427 						strlen(hyperobjects[hyperobjectcount].node);
428 					hyperobjects[hyperobjectcount].filelen =
429 						strlen(hyperobjects[hyperobjectcount].file);
430 					hyperobjectcount++;
431 				}
432 				else
433 				{
434 					goto handle_no_file_menu_label;
435 				}
436 			}
437 			else if (dot != NULL)	/* if not cross-info reference */
438 			{
439 handle_no_file_menu_label:
440 				{
441 					long NodenameLen;
442 					int goodHit = 0;	/* has val of 1, if it's a good hit */
443 					if (!hyperobjectcount)
444 						hyperobjects = xmalloc(sizeof(HyperObject));
445 					else
446 					{
447 						hyperobjects = xrealloc(hyperobjects,
448 								sizeof(HyperObject) *(hyperobjectcount + 1));
449 					}
450 
451 					start = tmp + 1;	/* move after the padding spaces */
452 					while (isspace(*start))
453 						start++;
454 					NodenameLen =(long)(dot - start);
455 					strcpy(hyperobjects[hyperobjectcount].file, "");
456 					strncpy(hyperobjects[hyperobjectcount].node,
457 							start, NodenameLen);
458 					hyperobjects[hyperobjectcount].node[NodenameLen] = 0;
459 					hyperobjects[hyperobjectcount].type = 1;
460 					hyperobjects[hyperobjectcount].line = line;
461 					hyperobjects[hyperobjectcount].col = calculate_len(line1, start);
462 					hyperobjects[hyperobjectcount].breakpos = -1;
463 					hyperobjects[hyperobjectcount].nodelen =
464 						strlen(hyperobjects[hyperobjectcount].node);
465 					hyperobjects[hyperobjectcount].filelen =
466 						strlen(hyperobjects[hyperobjectcount].file);
467 					if (exists_in_tag_table(hyperobjects[hyperobjectcount].node))
468 					{
469 						hyperobjectcount++;		/* yep, this was a good hit */
470 						goodHit = 1;
471 					}
472 					if (!goodHit && !hyperobjectcount)
473 					{
474 						xfree(hyperobjects);
475 						hyperobjects = 0;
476 					}
477 				}
478 			}
479 		}
480 	}
481 	/******************************************************************************
482 	 * Handle notes. In similar way as above.                                      *
483 	 ******************************************************************************/
484 	else if (    ( (notestart = strstr(buf, "*note")) != NULL )
485 		      || ( (notestart = strstr(buf, "*Note")) != NULL ) )
486 	{
487 handlenote:
488 		/*************************************************************************
489 		 * Scan for normal note of kind "*(infofile)reference:: comment", where  *
490 		 * the infofile parameter is optional, and indicates, that a reference   *
491 		 * matches different info file.                                          *
492 		 *************************************************************************/
493 		/* make sure that we don't handle notes, which fit in the second line */
494 		/* Signed-unsigned issues FIXME */
495 		if (notestart  < buf + strlen(line1))
496 		{
497 			/* we can handle only those, who are in the first line,
498 			 * or who are split up into two lines */
499 			tmp = strstr(notestart, "::");	/* "*note notelink:: comment" */
500 			if (tmp != NULL)
501 			{
502 				if (!hyperobjectcount)
503 					hyperobjects = xmalloc(sizeof(HyperObject));
504 				else
505 					hyperobjects = xrealloc(hyperobjects, sizeof(HyperObject) *(hyperobjectcount + 1));
506 				if (notestart[6] == '(')	/* if cross-info link */
507 				{
508 					char *end = strchr(notestart, ')');
509 					/* if the ')' char was found, and was before '::' */
510 					if ((end != NULL) &&(end < tmp))
511 					{
512 						long FilenameLen =(long)(end - notestart - 7);
513 						long NodenameLen =(long)(tmp - end - 1);
514 						strncpy(hyperobjects[hyperobjectcount].file,
515 								notestart + 7, FilenameLen);
516 						hyperobjects[hyperobjectcount].file[FilenameLen] = 0;
517 						strncpy(hyperobjects[hyperobjectcount].node,
518 								end + 1, NodenameLen);
519 						hyperobjects[hyperobjectcount].node[NodenameLen] = 0;
520 						hyperobjects[hyperobjectcount].type = 2;
521 						if (notestart + 7 < buf + strlen(line1))
522 						{
523 							hyperobjects[hyperobjectcount].line = line;
524 							hyperobjects[hyperobjectcount].col = calculate_len(buf, notestart + 7);
525 							/* if the note highlight fits int first line */
526 							if (tmp < buf + strlen(line1))
527 							{
528 								/* we don't need to break highlighting
529 								 * into several lines */
530 								hyperobjects[hyperobjectcount].breakpos = -1;
531 							}
532 							else
533 							{
534 								/* otherwise we need it */
535 								hyperobjects[hyperobjectcount].breakpos =
536 									strlen(line1) -(long)(notestart + 7 - buf) + 1;
537 							}
538 						}
539 						else
540 						{
541 							hyperobjects[hyperobjectcount].line = line + 1;
542 							hyperobjects[hyperobjectcount].col =
543 								calculate_len(buf + strlen(line1), notestart + 7);
544 							if (tmp < buf + strlen(line1))	/* as above */
545 								hyperobjects[hyperobjectcount].breakpos = -1;
546 							else if ((hyperobjects[hyperobjectcount].breakpos =
547 										strlen(line1) -
548 										(long)(notestart + 7 - buf) + 1) == 0)
549 							{
550 								hyperobjects[hyperobjectcount].line--;
551 							}
552 						}
553 						hyperobjects[hyperobjectcount].nodelen =
554 							strlen(hyperobjects[hyperobjectcount].node);
555 						hyperobjects[hyperobjectcount].filelen =
556 							strlen(hyperobjects[hyperobjectcount].file);
557 						hyperobjectcount++;
558 					}
559 				}
560 				else /* if not cross-info link */
561 				{
562 					long NodenameLen =(long)(tmp - notestart - 6);
563 					int goodHit = 0;
564 					strcpy(hyperobjects[hyperobjectcount].file, "");
565 					strncpy(hyperobjects[hyperobjectcount].node,
566 							notestart + 6, NodenameLen);
567 					hyperobjects[hyperobjectcount].node[NodenameLen] = 0;
568 					hyperobjects[hyperobjectcount].type = 2;
569 					hyperobjects[hyperobjectcount].nodelen =
570 						strlen(hyperobjects[hyperobjectcount].node);
571 					hyperobjects[hyperobjectcount].filelen =
572 						strlen(hyperobjects[hyperobjectcount].file);
573 					if (notestart + 7 < buf + strlen(line1))
574 					{
575 						hyperobjects[hyperobjectcount].line = line;
576 						hyperobjects[hyperobjectcount].col =
577 							calculate_len(buf, notestart + 7) - 1;
578 						/* if the note highlight fits int first line */
579 						if (tmp < buf + strlen(line1))
580 						{
581 							/* we don't need to break highlighting into
582 							 * several lines */
583 							hyperobjects[hyperobjectcount].breakpos = -1;
584 						}
585 						else
586 						{
587 							/* otherwise we need it */
588 							hyperobjects[hyperobjectcount].breakpos =
589 								strlen(line1) -(long)(notestart + 7 - buf) + 1;
590 						}
591 					}
592 					else
593 					{
594 						hyperobjects[hyperobjectcount].line = line + 1;
595 						hyperobjects[hyperobjectcount].col =
596 							calculate_len(buf + strlen(line1), notestart + 7) - 1;
597 						if (tmp < buf + strlen(line1))	/* as above */
598 							hyperobjects[hyperobjectcount].breakpos = -1;
599 						else if ((hyperobjects[hyperobjectcount].breakpos =
600 									strlen(line1)
601 									-(long)(notestart + 7 - buf) + 1) == 0)
602 						{
603 							hyperobjects[hyperobjectcount].line--;
604 						}
605 					}
606 					if (exists_in_tag_table(hyperobjects[hyperobjectcount].node))
607 					{
608 						hyperobjectcount++;	/* yep, this was a good hit */
609 						goodHit = 1;
610 					}
611 					if (!goodHit && !hyperobjectcount)
612 					{
613 						xfree(hyperobjects);
614 						hyperobjects = 0;
615 					}
616 				}
617 			}
618 			/*****************************************************************
619 			 * Scan for note references of form                              *
620 			 * "* Comment:[spaces](infofile)reference."                      *
621 			 *****************************************************************/
622 			else if ((tmp = strstr(notestart, ":")) != NULL)
623 			{
624 				char *start = 0, *end = 0, *dot = 0;
625 				dot = finddot(tmp + 1, NOTE_DOT);	/* find the trailing dot */
626 				if ( (dot != NULL) && ( dot + 7 < dot + strlen(dot)) )
627 				{
628 					if (strncmp(dot, ".info)", 6) == 0)
629 						dot = finddot(dot + 1, NOTE_DOT);
630 				}
631 				if (    ((start = strchr(tmp, '(')) != NULL)
632 					 && (dot != NULL)
633 					 && ((end = strchr(start, ')')) != NULL))
634 					/* end may be found only if start is nonNULL!!! */
635 				{
636 					if ( (start < dot) && (end < dot) )	/* security mechanism ;) */
637 					{
638 						long FilenameLen =(long)(end - start - 1);
639 						long NodenameLen =(long)(dot - end - 1);
640 						if (!hyperobjectcount)
641 							hyperobjects = xmalloc(sizeof(HyperObject));
642 						else
643 						{
644 							hyperobjects = xrealloc(hyperobjects,
645 									sizeof(HyperObject) *(hyperobjectcount + 1));
646 						}
647 						strncpy(hyperobjects[hyperobjectcount].file,
648 								start + 1, FilenameLen);
649 						hyperobjects[hyperobjectcount].file[FilenameLen] = 0;
650 						strncpy(hyperobjects[hyperobjectcount].node,
651 								end + 1, NodenameLen);
652 						hyperobjects[hyperobjectcount].node[NodenameLen] = 0;
653 						hyperobjects[hyperobjectcount].type = 3;
654 						if (start < buf + strlen(line1))
655 						{
656 							hyperobjects[hyperobjectcount].line = line;
657 							hyperobjects[hyperobjectcount].col =
658 								calculate_len(buf, start);
659 							if (dot < buf + strlen(line1))
660 							{
661 								/* if the note highlight fits in first line
662 								 * we don't need to break highlighting into
663 								 * several lines */
664 								hyperobjects[hyperobjectcount].breakpos = -1;
665 							}
666 							else
667 							{
668 								/* otherwise we need it */
669 								hyperobjects[hyperobjectcount].breakpos =
670 									strlen(line1) -(long)(start - buf);
671 							}
672 						}
673 						else
674 						{
675 							hyperobjects[hyperobjectcount].line = line + 1;
676 							hyperobjects[hyperobjectcount].col =
677 								calculate_len(buf + strlen(line1), start);
678 							hyperobjects[hyperobjectcount].breakpos = -1;
679 						}
680 						hyperobjects[hyperobjectcount].nodelen =
681 							strlen(hyperobjects[hyperobjectcount].node);
682 						hyperobjects[hyperobjectcount].filelen =
683 							strlen(hyperobjects[hyperobjectcount].file);
684 						hyperobjectcount++;
685 					}
686 					else
687 					{
688 						goto handle_no_file_note_label;
689 					}
690 				}
691 				else if (dot != NULL)	/* if not cross-info reference */
692 				{
693 handle_no_file_note_label:
694 					{
695 						long NodenameLen;
696 						int goodHit = 0;
697 						if (!hyperobjectcount)
698 							hyperobjects = xmalloc(sizeof(HyperObject));
699 						else
700 						{
701 							hyperobjects = xrealloc(hyperobjects,
702 									sizeof(HyperObject) *(hyperobjectcount + 1));
703 						}
704 
705 						start = tmp + 1;	/* move after the padding spaces */
706 						while (isspace(*start))
707 							start++;
708 						NodenameLen =(long)(dot - start);
709 						strcpy(hyperobjects[hyperobjectcount].file, "");
710 						strncpy(hyperobjects[hyperobjectcount].node,
711 								start, NodenameLen);
712 						hyperobjects[hyperobjectcount].node[NodenameLen] = 0;
713 						hyperobjects[hyperobjectcount].type = 3;
714 						hyperobjects[hyperobjectcount].nodelen =
715 							strlen(hyperobjects[hyperobjectcount].node);
716 						hyperobjects[hyperobjectcount].filelen =
717 							strlen(hyperobjects[hyperobjectcount].file);
718 						if (start < buf + strlen(line1))
719 						{
720 							hyperobjects[hyperobjectcount].line = line;
721 							hyperobjects[hyperobjectcount].col =
722 								calculate_len(buf, start);
723 							if (dot < buf + strlen(line1))
724 							{
725 								/* if the note highlight fits in first line
726 								 * we don't need to break highlighting into
727 								 * several lines */
728 								hyperobjects[hyperobjectcount].breakpos = -1;
729 							}
730 							else
731 							{
732 								/* otherwise we need it */
733 								hyperobjects[hyperobjectcount].breakpos =
734 									strlen(line1) -(long)(start - buf);
735 							}
736 						}
737 						else
738 						{
739 							hyperobjects[hyperobjectcount].line = line + 1;
740 							hyperobjects[hyperobjectcount].col =
741 								calculate_len(strlen(line1) + buf, start);
742 							hyperobjects[hyperobjectcount].breakpos = -1;
743 						}
744 						if (exists_in_tag_table(hyperobjects[hyperobjectcount].node))
745 						{
746 							hyperobjectcount++;	/* yep, this was a good hit */
747 							goodHit = 1;
748 						}
749 						if (!goodHit && !hyperobjectcount)
750 						{
751 							xfree(hyperobjects);
752 							hyperobjects = 0;
753 						}
754 					}
755 				}
756 			}
757 		}
758 	}
759 	if (notestart)
760 		if (notestart + 6 < buf + strlen(buf) + 1)
761 		{
762 			tmp = notestart;
763 			if ((notestart = strstr(notestart + 6, "*Note")) != NULL)
764 				goto handlenote;
765 			notestart = tmp;
766 			if ((notestart = strstr(notestart + 6, "*note")) != NULL)
767 				goto handlenote;
768 		}
769 	/******************************************************************************
770 	 * Try to scan for some url-like objects in single line; mainly               *
771 	 * http://[address][space|\n|\t]                                              *
772 	 * ftp://[address][space|\n|\t]                                               *
773 	 * username@something.else[space|\n|\t]                                       *
774 	 *****************************************************************************/
775 	/* http:// */
776 	urlend = line1;
777 	while ((urlstart = strstr(urlend, "http://")) != NULL)
778 	{
779 		urlend = findurlend(urlstart);	/* always successful */
780 		if (!hyperobjectcount)
781 			hyperobjects = xmalloc(sizeof(HyperObject));
782 		else
783 		{
784 			hyperobjects = xrealloc(hyperobjects,
785 					sizeof(HyperObject) *(hyperobjectcount + 1));
786 		}
787 		hyperobjects[hyperobjectcount].line = line;
788 		hyperobjects[hyperobjectcount].col = calculate_len(line1, urlstart);
789 		hyperobjects[hyperobjectcount].breakpos = -1;
790 		hyperobjects[hyperobjectcount].type = 4;
791 		strncpy(hyperobjects[hyperobjectcount].node, urlstart, urlend - urlstart);
792 		hyperobjects[hyperobjectcount].node[urlend - urlstart] = 0;
793 		strcpy(hyperobjects[hyperobjectcount].file, "");
794 		hyperobjects[hyperobjectcount].tagtableoffset = -1;
795 		hyperobjects[hyperobjectcount].nodelen =
796 			strlen(hyperobjects[hyperobjectcount].node);
797 		hyperobjects[hyperobjectcount].filelen =
798 			strlen(hyperobjects[hyperobjectcount].file);
799 		hyperobjectcount++;
800 	}
801 	/* ftp:// */
802 	urlend = line1;
803 	while ((urlstart = strstr(urlend, "ftp://")) != NULL)
804 	{
805 		urlend = findurlend(urlstart);	/* always successful */
806 		if (!hyperobjectcount)
807 			hyperobjects = xmalloc(sizeof(HyperObject));
808 		else
809 		{
810 			hyperobjects = xrealloc(hyperobjects,
811 					sizeof(HyperObject) *(hyperobjectcount + 1));
812 		}
813 		hyperobjects[hyperobjectcount].line = line;
814 		hyperobjects[hyperobjectcount].col = calculate_len(line1, urlstart);
815 		hyperobjects[hyperobjectcount].breakpos = -1;
816 		hyperobjects[hyperobjectcount].type = 5;
817 		strncpy(hyperobjects[hyperobjectcount].node, urlstart, urlend - urlstart);
818 		hyperobjects[hyperobjectcount].node[urlend - urlstart] = 0;
819 		strcpy(hyperobjects[hyperobjectcount].file, "");
820 		hyperobjects[hyperobjectcount].tagtableoffset = -1;
821 		hyperobjects[hyperobjectcount].nodelen =
822 			strlen(hyperobjects[hyperobjectcount].node);
823 		hyperobjects[hyperobjectcount].filelen =
824 			strlen(hyperobjects[hyperobjectcount].file);
825 		hyperobjectcount++;
826 	}
827 	if (initialhyperobjectcount != hyperobjectcount)
828 		sort_hyperlinks_from_current_line(initialhyperobjectcount, hyperobjectcount);
829 	if (buf)
830 	{
831 		xfree(buf);
832 		buf = 0;
833 	}
834 }
835