1 /*
2 Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
3 Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
4 Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
5
6 This file is part of ROBODoc
7
8 ROBODoc is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
20
21 */
22
23 /****h* ROBODoc/Links
24 * FUNCTION
25 * This module contains functions to manipulate links.
26 * Links are derived from headers. They are used to create
27 * links in the documentation between a word and the part of
28 * the documentation that explains something about that word.
29 * (For instance a function name or variable name).
30 * In addition to the links derived from the headers links are
31 * also derived from the names of all the sourcefiles.
32 * MODIFICATION HISTORY
33 * ????-??-?? Frans Slothouber V1.0
34 * 2003-02-03 Frans Slothouber Refactoring
35 *******
36 * $Header: /cvsroot/robodoc/robo/Source/links.c,v 1.43 2007/07/10 19:13:52 gumpu Exp $
37 */
38
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <ctype.h>
43 #include <assert.h>
44
45 #include "globals.h"
46 #include "robodoc.h"
47 #include "headers.h"
48 #include "util.h"
49 #include "links.h"
50 #include "document.h"
51 #include "part.h"
52 #include "file.h"
53
54 #ifdef DMALLOC
55 #include <dmalloc.h>
56 #endif
57
58 /* TODO Documentation */
59 unsigned int link_index_size = 0;
60 struct RB_link **link_index = NULL;
61 struct RB_link **case_sensitive_link_index = NULL;
62
63
64 /* Local functions */
65
66 static struct RB_link *RB_Alloc_Link( char *label_name, char *object_name,
67 char *file_name );
68
69 /* TODO Documentation */
link_cmp(void * l1,void * l2)70 int link_cmp( void *l1, void *l2 )
71 {
72 struct RB_link *link1 = l1;
73 struct RB_link *link2 = l2;
74
75 return RB_Str_Case_Cmp( link1->object_name, link2->object_name );
76 }
77
78
case_sensitive_link_cmp(void * l1,void * l2)79 int case_sensitive_link_cmp( void *l1, void *l2 )
80 {
81 struct RB_link *link1 = l1;
82 struct RB_link *link2 = l2;
83
84 return strcmp( link1->object_name, link2->object_name );
85 }
86
function_name(char * full_name)87 char * function_name( char * full_name )
88 {
89 char * name = strchr( full_name, '/' );
90
91 if( name ) return name + 1;
92 else return full_name;
93 }
94
95 /****f* Links/RB_CollectLinks
96 * FUNCTION
97 * Convert header information into link information.
98 * RB_header -> RB_link conversion
99 * SYNOPSIS
100 */
101 void
RB_CollectLinks(struct RB_Document * document,struct RB_header ** headers,unsigned long count)102 RB_CollectLinks( struct RB_Document *document,
103 struct RB_header **headers,
104 unsigned long count )
105 /*
106 * INPUTS
107 * * document --
108 * * headers -- the array with headers.
109 * * count -- number of headers in the array
110 * OUTPUT
111 * * link_index -- an array with links
112 * * link_index_size -- the number of links in the array.
113 * SOURCE
114 */
115
116 {
117 unsigned long i, j;
118 int k;
119 struct RB_Part *i_part;
120
121 for ( i = j = 0; i < count; ++i )
122 {
123 j += headers[i]->no_names - 1;
124 }
125
126 link_index_size = count + j;
127
128 if ( ( document->actions.do_multidoc ) &&
129 ! ( document->actions.do_one_file_per_header )
130 )
131 {
132 for ( i_part = document->parts; i_part; i_part = i_part->next )
133 {
134 if ( i_part->headers )
135 {
136 link_index_size++;
137 }
138 }
139 }
140
141 link_index =
142 ( struct RB_link ** ) calloc( link_index_size,
143 sizeof( struct RB_link ** ) );
144 case_sensitive_link_index =
145 ( struct RB_link ** ) calloc( link_index_size,
146 sizeof( struct RB_link ** ) );
147
148 for ( i = j = 0; i < count; ++i )
149 {
150 struct RB_link *link;
151 struct RB_header *header;
152
153 header = headers[i];
154 assert( header->unique_name );
155 assert( header->file_name );
156 for( k = 0; k < header->no_names; j++, k++ )
157 {
158 link = RB_Alloc_Link( header->unique_name, function_name(header->names[k]),
159 header->file_name );
160 link->htype = header->htype;
161 link->is_internal = header->is_internal;
162 link_index[j] = link;
163 case_sensitive_link_index[j] = link;
164 }
165 }
166
167 /* If we are in multidoc mode, we also create links
168 * for all the source files.
169 */
170
171 if ( ( document->actions.do_multidoc ) &&
172 /* but not for one file per header multidocs */
173 ! ( document->actions.do_one_file_per_header )
174 )
175 {
176 for ( i_part = document->parts; i_part; i_part = i_part->next )
177 {
178 if ( i_part->headers )
179 {
180 struct RB_link *link;
181
182 link =
183 RB_Alloc_Link( "robo_top_of_doc", i_part->filename->name,
184 RB_Get_FullDocname( i_part->filename ) );
185 i_part->filename->link = link;
186 link->htype = RB_FindHeaderType( HT_SOURCEHEADERTYPE );
187 link_index[j] = link;
188 case_sensitive_link_index[j] = link;
189 ++j;
190 }
191 else
192 {
193 i_part->filename->link = NULL;
194 }
195 }
196 }
197
198 /* Sort all the links so we can use a binary search */
199 RB_QuickSort( (void **)link_index, 0, link_index_size - 1, link_cmp );
200 RB_QuickSort( (void **)case_sensitive_link_index, 0, link_index_size - 1, case_sensitive_link_cmp );
201 }
202
203 /*****/
204
205
206 /****f* Links/RB_Free_Links
207 * FUNCTION
208 * Deallocate all the memory used to store the links.
209 * SYNOPSIS
210 */
RB_Free_Links(void)211 void RB_Free_Links( void )
212 /*
213 * INPUTS
214 * o link_index_size
215 * o link_index[]
216 * BUGS
217 * Should use RB_Free_Link instead of doing
218 * everything by it self.
219 * SOURCE
220 */
221 {
222 struct RB_link *cur_link;
223 unsigned int i;
224
225 for ( i = 0; i < link_index_size; ++i )
226 {
227 cur_link = link_index[i];
228 free( cur_link->object_name );
229 free( cur_link->label_name );
230 free( cur_link->file_name );
231 free( cur_link );
232 }
233 free( link_index );
234 }
235
236 /*******/
237
238 /* TODO Documentation */
239
RB_Number_Of_Links(struct RB_HeaderType * header_type,char * file_name,int internal)240 int RB_Number_Of_Links( struct RB_HeaderType* header_type, char* file_name, int internal )
241 {
242 struct RB_link *cur_link;
243 int n = 0;
244 unsigned int i;
245
246 for ( i = 0; i < link_index_size; ++i )
247 {
248 cur_link = link_index[i];
249 if ( RB_CompareHeaderTypes( cur_link->htype, header_type ) &&
250 ( ( cur_link->is_internal && internal ) ||
251 ( !cur_link->is_internal && !internal ) ) )
252 {
253 if ( file_name )
254 {
255 if ( strcmp( file_name, cur_link->file_name ) == 0 )
256 {
257 n++;
258 }
259 }
260 else
261 {
262 n++;
263 }
264 }
265 }
266 return n;
267 }
268
269 /****f* Links/Find_Link [3.0h]
270 * NAME
271 * Find_Link -- try to match word with a link
272 * FUNCTION
273 * Searches for the given word in the list of links and
274 * headers. There are three passes (or four, when the C option
275 * is selected). Each pass uses a different definition of "word":
276 * o In the first pass it is any thing that ends with a 'space', a '.'
277 * or a ','.
278 * o In the second pass it is any string that consists of alpha
279 * numerics, '_', ':', '.', or '-'.
280 * o In the third pass (for C) it is any string that consists
281 * of alpha numerics or '_' (and '-' with --cobol).
282 * SYNOPSIS
283 */
284
285 int
Find_Link(char * word_begin,char ** object_name,char ** label_name,char ** file_name)286 Find_Link( char *word_begin,
287 char **object_name,
288 char **label_name,
289 char **file_name )
290 /*
291 * INPUTS
292 * o word_begin - pointer to a word (a string).
293 * o object_name - pointer to a pointer to a string
294 * o file_name - pointer to a pointer to a string
295 * SIDE EFFECTS
296 * label_name & file_name are modified
297 * RESULT
298 * o object_name -- points to the object if a match was found,
299 * NULL otherwise.
300 * o file_name -- points to the file name if a match was found,
301 * NULL otherwise.
302 * o label_name -- points to the labelname if a match was found,
303 * o TRUE -- a match was found.
304 * o FALSE -- no match was found.
305 * NOTES
306 * This is a rather sensitive algorithm. Don't mess with it
307 * too much.
308 * SOURCE
309 */
310
311 {
312 char *cur_char = NULL, old_char;
313 int low_index, high_index, cur_index, state, pass;
314 unsigned int length = 0;
315
316 for ( pass = 0; pass < 3; pass++ )
317 {
318 switch ( pass )
319 {
320 case 0:
321 {
322 for ( cur_char = word_begin;
323 ( *cur_char == '_' ) || ( course_of_action.do_hyphens && *cur_char == '-' ) || utf8_isalnum( *cur_char ) || utf8_ispunct( *cur_char );
324 cur_char++ );
325 break;
326 }
327 case 1:
328 {
329 for ( cur_char = word_begin;
330 utf8_isalnum( *cur_char ) || ( *cur_char == '_' ) ||
331 ( course_of_action.do_hyphens && *cur_char == '-' ) || ( *cur_char == '.' ) ||
332 ( *cur_char == ':' ); cur_char++ );
333 break;
334 }
335 case 2:
336 {
337 for ( cur_char = word_begin;
338 utf8_isalnum( *cur_char ) || ( *cur_char == '_' ) || ( course_of_action.do_hyphens && *cur_char == '-' );
339 cur_char++ );
340 break;
341 }
342 }
343
344 if ( ( ( *( cur_char - 1 ) ) == ',' ) || ( ( *( cur_char - 1 ) ) == '.' ) )
345 {
346 cur_char--;
347 }
348
349 old_char = *cur_char;
350 *cur_char = '\0'; /*
351 * End the word with a '\0'
352 */
353 if ( strlen( word_begin ) == length )
354 {
355 /* Do not test the same word over and over. If
356 * the current string and the string of the previous
357 * pass are the same length, they are the same. */
358
359 /* RB_Say ("Skipping (pass %d) \"%s\"\n", SAY_INFO, pass, word_begin); */
360 }
361 else
362 {
363 length = strlen( word_begin );
364 /* RB_Say ("Testing (pass %d) \"%s\"\n", SAY_INFO, pass, word_begin); */
365 /*
366 * Search case sensitive for a link
367 */
368 for ( cur_index = 0, low_index = 0, high_index =
369 link_index_size - 1; high_index >= low_index; )
370 {
371 cur_index = ( high_index - low_index ) / 2 + low_index;
372 state = strcmp( word_begin, case_sensitive_link_index[cur_index]->object_name );
373 if ( state < 0 )
374 {
375 high_index = cur_index - 1;
376 }
377 else if ( state == 0 )
378 {
379 *object_name = case_sensitive_link_index[cur_index]->object_name;
380 *label_name = case_sensitive_link_index[cur_index]->label_name;
381 *file_name = case_sensitive_link_index[cur_index]->file_name;
382 RB_Say( "linking \"%s\"->\"%s\" from \"%s\"\n", SAY_DEBUG,
383 word_begin, *object_name, *file_name );
384 *cur_char = old_char;
385 return ( TRUE );
386 }
387 else if ( state > 0 )
388 {
389 low_index = cur_index + 1;
390 }
391 }
392
393 /*
394 * Search case insensitive for a link.
395 * But only when the user asks for this.
396 */
397 if ( course_of_action.do_ignore_case_when_linking )
398 {
399
400 for ( cur_index = 0, low_index = 0, high_index =
401 link_index_size - 1; high_index >= low_index; )
402 {
403 cur_index = ( high_index - low_index ) / 2 + low_index;
404 state = RB_Str_Case_Cmp( word_begin, link_index[cur_index]->object_name );
405 if ( state < 0 )
406 {
407 high_index = cur_index - 1;
408 }
409 else if ( state == 0 )
410 {
411 *object_name = link_index[cur_index]->object_name;
412 *label_name = link_index[cur_index]->label_name;
413 *file_name = link_index[cur_index]->file_name;
414 RB_Say( "linking \"%s\"->\"%s\" from \"%s\"\n", SAY_DEBUG,
415 word_begin, *object_name, *file_name );
416 *cur_char = old_char;
417 return ( TRUE );
418 }
419 else if ( state > 0 )
420 {
421 low_index = cur_index + 1;
422 }
423 }
424 }
425 }
426 *cur_char = old_char;
427 *file_name = NULL;
428 *object_name = NULL;
429 *label_name = NULL;
430 }
431 return ( FALSE );
432 }
433
434 /*****/
435
436
437 /****f* Links/RB_Alloc_Link [2.01]
438 * NAME
439 * RB_Alloc_Link -- oop
440 * FUNCTION
441 * allocate struct + strings
442 * SYNOPSIS
443 */
444 static struct RB_link *
RB_Alloc_Link(char * label_name,char * object_name,char * file_name)445 RB_Alloc_Link( char *label_name, char *object_name, char *file_name )
446 /*
447 * INPUTS
448 * char *label_name -- strings to copy into the link
449 * char *file_name
450 * RESULT
451 * struct RB_link * -- ready-to-use
452 * AUTHOR
453 * Koessi
454 * SEE ALSO
455 * RB_StrDup(), RB_Free_Link()
456 * SOURCE
457 */
458
459 {
460 struct RB_link *new_link;
461
462 assert( object_name );
463 assert( label_name );
464 assert( file_name );
465 RB_Say( "Allocating a link (%s %s %s)\n", SAY_DEBUG, object_name, label_name, file_name );
466 new_link = malloc( sizeof( struct RB_link ) );
467 memset( new_link, 0, sizeof( struct RB_link ) );
468
469 new_link->file_name = RB_StrDup( file_name );
470 new_link->object_name = RB_StrDup( object_name );
471 new_link->label_name = RB_StrDup( label_name );
472 return ( new_link );
473 }
474
475 /*****/
476
477 /****f* Links/RB_Free_Link
478 * NAME
479 * RB_Free_Link -- oop
480 * FUNCTION
481 * free struct + strings
482 * SYNOPSIS
483 */
RB_Free_Link(struct RB_link * arg_link)484 void RB_Free_Link( struct RB_link *arg_link )
485 /*
486 * INPUTS
487 * struct RB_link *link
488 * AUTHOR
489 * Koessi
490 * SEE ALSO
491 * RB_Alloc_Link(), RB_Close_The_Shop()
492 * SOURCE
493 */
494
495 {
496 if ( arg_link )
497 {
498 if ( arg_link->label_name )
499 {
500 free( arg_link->label_name );
501 }
502 if ( arg_link->file_name )
503 {
504 free( arg_link->file_name );
505 }
506 free( arg_link );
507 }
508 }
509
510 /******/
511
512