Added options to make Robodoc more customizable.
[robodoc.git] / Source / links.c
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 */
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
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
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
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,
159                                   header->function_name,
160                                   header->file_name );
161             link->htype = header->htype;
162             link->is_internal = header->is_internal;
163             link_index[j] = link;
164             case_sensitive_link_index[j] = link;
165         }
166     }
167
168     /* If we are in multidoc mode, we also create links
169      * for all the source files.
170      */
171
172     if ( ( document->actions.do_multidoc ) &&
173             /* but not for one file per header multidocs */
174        ! ( document->actions.do_one_file_per_header )
175        )
176     {
177         for ( i_part = document->parts; i_part; i_part = i_part->next )
178         {
179             if ( i_part->headers )
180             {
181                 struct RB_link     *link;
182
183                 link =
184                     RB_Alloc_Link( "robo_top_of_doc", i_part->filename->name,
185                             RB_Get_FullDocname( i_part->filename ) );
186                 i_part->filename->link = link;
187                 link->htype = RB_FindHeaderType( HT_SOURCEHEADERTYPE );
188                 link_index[j] = link;
189                 case_sensitive_link_index[j] = link;
190                 ++j;
191             }
192             else
193             {
194                 i_part->filename->link = NULL;
195             }
196         }
197     }
198
199     /* Sort all the links so we can use a binary search */
200     RB_QuickSort( (void **)link_index, 0, link_index_size - 1, link_cmp );
201     RB_QuickSort( (void **)case_sensitive_link_index, 0, link_index_size - 1, case_sensitive_link_cmp );
202 }
203
204 /*****/
205
206
207 /****f* Links/RB_Free_Links
208  * FUNCTION
209  *   Deallocate all the memory used to store the links.
210  * SYNOPSIS
211  */
212 void RB_Free_Links( void )
213 /*
214  * INPUTS
215  *   o link_index_size
216  *   o link_index[]
217  * BUGS
218  *   Should use RB_Free_Link instead of doing
219  *   everything by it self.
220  * SOURCE
221  */
222 {
223     struct RB_link     *cur_link;
224     unsigned int        i;
225
226     for ( i = 0; i < link_index_size; ++i )
227     {
228         cur_link = link_index[i];
229         free( cur_link->object_name );
230         free( cur_link->label_name );
231         free( cur_link->file_name );
232         free( cur_link );
233     }
234     free( link_index );
235 }
236
237 /*******/
238
239 /* TODO Documentation */
240
241 int RB_Number_Of_Links( struct RB_HeaderType* header_type, char* file_name, int internal )
242 {
243     struct RB_link     *cur_link;
244     int                 n = 0;
245     unsigned int        i;
246
247     for ( i = 0; i < link_index_size; ++i )
248     {
249         cur_link = link_index[i];
250         if ( RB_CompareHeaderTypes( cur_link->htype, header_type ) &&
251              ( ( cur_link->is_internal && internal ) ||
252                ( !cur_link->is_internal && !internal ) ) )
253         {
254             if ( file_name )
255             {
256                 if ( strcmp( file_name, cur_link->file_name ) == 0 )
257                 {
258                     n++;
259                 }
260             }
261             else
262             {
263                 n++;
264             }
265         }
266     }
267     return n;
268 }
269
270 /****f* Links/Find_Link [3.0h]
271  * NAME
272  *   Find_Link -- try to match word with a link
273  * FUNCTION
274  *   Searches for the given word in the list of links and
275  *   headers.  There are three passes (or four, when the C option
276  *   is selected). Each pass uses a different definition of "word":
277  *   o In the first pass it is any thing that ends with a 'space', a '.'
278  *     or a ','.
279  *   o In the second pass it is any string that consists of alpha
280  *     numerics, '_', ':', '.', or '-'.
281  *   o In the third pass (for C) it is any string that consists
282  *     of alpha numerics or '_'.
283  * SYNOPSIS
284  */
285
286 int
287 Find_Link( char *word_begin,
288            char **object_name,
289            char **label_name,
290            char **file_name )
291 /*
292  * INPUTS
293  *   o word_begin  - pointer to a word (a string).
294  *   o object_name  - pointer to a pointer to a string
295  *   o file_name   - pointer to a pointer to a string
296  * SIDE EFFECTS
297  *   label_name & file_name are modified
298  * RESULT
299  *   o object_name   -- points to the object if a match was found,
300  *                      NULL otherwise.
301  *   o file_name     -- points to the file name if a match was found,
302  *                      NULL otherwise.
303  *   o label_name    -- points to the labelname if a match was found,
304  *   o TRUE          -- a match was found.
305  *   o FALSE         -- no match was found.
306  * NOTES
307  *   This is a rather sensitive algorithm. Don't mess with it
308  *   too much.
309  * SOURCE
310  */
311
312 {
313     char               *cur_char = NULL, old_char;
314     int                 low_index, high_index, cur_index, state, pass;
315     unsigned int        length = 0;
316
317     for ( pass = 0; pass < 3; pass++ )
318     {
319         switch ( pass )
320         {
321         case 0:
322             {
323                 for ( cur_char = word_begin;
324                       ( *cur_char == '_' ) || utf8_isalnum( *cur_char ) || utf8_ispunct( *cur_char );
325                       cur_char++ );
326                 break;
327             }
328         case 1:
329             {
330                 for ( cur_char = word_begin;
331                       utf8_isalnum( *cur_char ) || ( *cur_char == '_' ) ||
332                       ( *cur_char == '-' ) || ( *cur_char == '.' ) ||
333                       ( *cur_char == ':' ); cur_char++ );
334                 break;
335             }
336         case 2:
337             {
338                 for ( cur_char = word_begin;
339                       utf8_isalnum( *cur_char ) || ( *cur_char == '_');
340                       cur_char++ );
341                 break;
342             }
343         }
344
345         if ( ( ( *( cur_char - 1 ) ) == ',' ) || ( ( *( cur_char - 1 ) ) == '.' ) )
346         {
347             cur_char--;
348         }
349
350         old_char = *cur_char;
351         *cur_char = '\0';       /*
352                                  * End the word with a '\0'
353                                  */
354         if ( strlen( word_begin ) == length )
355         {
356             /* Do not test the same word over and over. If
357              * the current string and the string of the previous
358              * pass are the same length, they are the same. */
359
360             /* RB_Say ("Skipping (pass %d) \"%s\"\n", SAY_INFO, pass, word_begin);  */
361         }
362         else
363         {
364             length = strlen( word_begin );
365             /* RB_Say ("Testing (pass %d) \"%s\"\n", SAY_INFO, pass, word_begin); */
366             /*
367              * Search case sensitive for a link
368              */
369             for ( cur_index = 0, low_index = 0, high_index =
370                     link_index_size - 1; high_index >= low_index; )
371             {
372                 cur_index = ( high_index - low_index ) / 2 + low_index;
373                 state = strcmp( word_begin, case_sensitive_link_index[cur_index]->object_name );
374                 if ( state < 0 )
375                 {
376                     high_index = cur_index - 1;
377                 }
378                 else if ( state == 0 )
379                 {
380                     *object_name = case_sensitive_link_index[cur_index]->object_name;
381                     *label_name = case_sensitive_link_index[cur_index]->label_name;
382                     *file_name = case_sensitive_link_index[cur_index]->file_name;
383                     RB_Say( "linking \"%s\"->\"%s\" from \"%s\"\n", SAY_DEBUG,
384                             word_begin, *object_name, *file_name );
385                     *cur_char = old_char;
386                     return ( TRUE );
387                 }
388                 else if ( state > 0 )
389                 {
390                     low_index = cur_index + 1;
391                 }
392             }
393
394             /*
395              * Search case insensitive for a link.
396              * But only when the user asks for this.
397              */
398             if ( course_of_action.do_ignore_case_when_linking )
399             {
400
401                 for ( cur_index = 0, low_index = 0, high_index =
402                         link_index_size - 1; high_index >= low_index; )
403                 {
404                     cur_index = ( high_index - low_index ) / 2 + low_index;
405                     state = RB_Str_Case_Cmp( word_begin, link_index[cur_index]->object_name );
406                     if ( state < 0 )
407                     {
408                         high_index = cur_index - 1;
409                     }
410                     else if ( state == 0 )
411                     {
412                         *object_name = link_index[cur_index]->object_name;
413                         *label_name = link_index[cur_index]->label_name;
414                         *file_name = link_index[cur_index]->file_name;
415                         RB_Say( "linking \"%s\"->\"%s\" from \"%s\"\n", SAY_DEBUG,
416                                 word_begin, *object_name, *file_name );
417                         *cur_char = old_char;
418                         return ( TRUE );
419                     }
420                     else if ( state > 0 )
421                     {
422                         low_index = cur_index + 1;
423                     }
424                 }
425             }
426         }
427         *cur_char = old_char;
428         *file_name = NULL;
429         *object_name = NULL;
430         *label_name = NULL;
431     }
432     return ( FALSE );
433 }
434
435 /*****/
436
437
438 /****f* Links/RB_Alloc_Link [2.01]
439  * NAME
440  *   RB_Alloc_Link              -- oop
441  * FUNCTION
442  *   allocate struct + strings
443  * SYNOPSIS
444  */
445 static struct RB_link *
446 RB_Alloc_Link( char *label_name, char *object_name, char *file_name )
447 /*
448  * INPUTS
449  *   char *label_name -- strings to copy into the link
450  *   char *file_name
451  * RESULT
452  *   struct RB_link *  -- ready-to-use
453  * AUTHOR
454  *   Koessi
455  * SEE ALSO
456  *   RB_StrDup(), RB_Free_Link()
457  * SOURCE
458  */
459
460 {
461     struct RB_link     *new_link;
462
463     assert( object_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 = label_name ? RB_StrDup( label_name ) : NULL;
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  */
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 /******/