Imported Robodoc.
[robodoc.git] / Source / robohdrs.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 /* $Id: robohdrs.c,v 1.14 2007/07/10 19:13:52 gumpu Exp $ */
24
25 /****h* Docuwala/ROBOhdrs
26 *  NAME
27 *    robohdrs
28 *  DESCRIPTION
29 *    Standalone program to insert ROBODoc headers to source code files.
30 *    This program processes one source file at the time. Existing 
31 *    ROBODoc headers, if any, are not checked for. Beware since this 
32 *    may result in double headers. Current working directory should 
33 *    be the same as where the source file is located.
34 *  USES
35 *    Exuberant Ctags 5.3.1 or newer required
36 *  USAGE
37 *    robohdrs [options] <source file>
38 *  EXAMPLE
39 *    robohdrs -p myproj test1.c
40 *    robohdrs -s -p myproj -i "MODIFICATION HISTORY" -i IDEAS test2.c
41 *
42 *    Type `robohdrs -h' to see all command line options.
43 *  TODO
44 *    - garbage collection
45 *    - support for other languages which ctags program supports
46 *  SEE ALSO
47 *    ROBODoc         https://sourceforge.net/projects/robodoc/
48 *    Exuberant Ctags http://ctags.sourceforge.net/
49 *  COPYRIGHT
50 *    (c) 2003 Frans Slothouber and Petteri Kettunen
51 *    Copying policy: GPL
52 *  HISTORY
53 *    2003-08-08/petterik: #define `d' header (bug rep. from Anand Dhanakshirur)
54 *    2003-08-04/petterik: -t option (suggestion from Anand Dhanakshirur)
55 *    2003-02-21/petterik: -l option, script option tested
56 *******
57 */
58
59 #ifdef HAVE_CONFIG_H
60 #include "config.h"
61 #endif
62
63
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <string.h>
67
68
69
70 #ifndef HAVE_FORK
71
72 int main( int argc, char** argv )
73 {
74     printf( "Not supported on this platform\n" );
75     printf( "This program requires fork()\n" );
76     return EXIT_SUCCESS;
77 }
78
79 #else
80
81
82
83 #include <assert.h>
84 #include <unistd.h>
85 #include <fcntl.h>
86 #include "headers.h"
87
88 /****v* ROBOhdrs/PROGNAME
89 *  NAME
90 *    PROGNAME
91 *  SOURCE
92 */
93 #define PROGNAME "robohdrs"
94 /********** PROGNAME */
95 /****v* ROBOhdrs/PROGVERSION
96 *  NAME
97 *    PROGVERSION
98 *  SOURCE
99 */
100 #define PROGVERSION "0.01"
101 /********** PROGVERSION */
102 /****v* ROBOhdrs/MAXLINE
103 *  NAME
104 *    MAXLINE
105 *  SOURCE
106 */
107 #define MAXLINE 10240
108 /********** MAXLINE */
109 /****v* ROBOhdrs/MAXNAME
110 *  NAME
111 *    MAXNAME
112 *  SOURCE
113 */
114 #define MAXNAME 1024
115
116 /********** MAXNAME */
117
118 /****v* ROBOhdrs/ctagsBin
119 *  NAME
120 *    ctagsBin
121 *  SOURCE
122 */
123 static char         ctagsBin[MAXNAME];
124
125 /********** ctagsBin */
126
127 /****v* ROBOhdrs/srcSta
128 *  NAME
129 *    srcSta
130 *  SEE ALSO
131 *    src_constants
132 *  SOURCE
133 */
134
135 static short        srcSta = SRC_C;
136 static short        srcRem = SRC_R_C;
137 static short        srcEnd = SRC_E_C;
138
139 /********** srcSta */
140
141 typedef struct _ctag_t
142 {
143     void               *prev;
144     void               *next;
145     char                fname[MAXNAME];
146     char                name[MAXNAME];
147     char                decl[MAXLINE];
148     char                type[MAXNAME];
149     int                 linenum;
150 }
151 ctag_t;
152
153 typedef struct _custhdr_t
154 {
155     void               *next;
156     char                name[MAXNAME];
157 }
158 custhdr_t;
159
160 typedef struct _ctags_t
161 {
162     ctag_t             *ctag;
163     int                 cnt;
164 }
165 ctags_t;
166
167 /****v* ROBOhdrs/myctags
168 *  NAME
169 *    myctags
170 *  SYNOPSIS
171 *    static ctags_t myctags;
172 *  SOURCE
173 */
174 static ctags_t      myctags;
175
176 /********** myctags */
177 /****v* ROBOhdrs/ctags
178 *  NAME
179 *    ctags
180 *  SYNOPSIS
181 *    static ctags_t *ctags;
182 *  SOURCE
183 */
184 static ctags_t     *ctags;
185
186 /********** ctags */
187 /****v* ROBOhdrs/custhdrs
188 *  NAME
189 *    custhdrs
190 *  SYNOPSIS
191 *    static custhdr_t *custhdrs = 0;
192 *  SOURCE
193 */
194 static custhdr_t   *custhdrs = 0;
195
196 /********** custhdrs */
197 /****v* ROBOhdrs/projName
198 *  NAME
199 *    projName
200 *  SYNOPSIS
201 *    static char projName[MAXNAME];
202 *  SOURCE
203 */
204 static char         projName[MAXNAME];
205
206 /********** projName */
207 /****v* ROBOhdrs/vcTag
208 *  NAME
209 *    vcTag
210 *  DESCRIPTION
211 *    Version control tag string. This is always specified by the user.
212 *  SYNOPSIS
213 *    static char vcTag[MAXNAME];
214 *  SOURCE
215 */
216 static char         vcTag[MAXNAME];
217
218 /********** projName */
219 /****v* ROBOhdrs/incSrc
220 *  NAME
221 *    incSrc
222 *  SYNOPSIS
223 *    static char incSrc = 0;
224 *  SOURCE
225 */
226 static char         incSrc = 0;
227
228 /********** incSrc */
229 /****f* ROBOhdrs/usage
230 *  NAME
231 *    usage
232 *  SYNOPSIS
233 *    static void usage(void)
234 *  SOURCE
235 */
236 static void
237 usage( void )
238 {
239     printf( "%s version %s, robodoc header insertor\n", PROGNAME,
240             PROGVERSION );
241     printf( "(c) 2003 Frans Slothouber and Petteri Kettunen\n" );
242     printf( "%s comes with ABSOLUTELY NO WARRANTY.\n", PROGNAME );
243     printf
244         ( "This is free software, and you are welcome to redistribute it\n" );
245     printf( "under the GNU GENERAL PUBLIC LICENSE terms and conditions.\n" );
246     printf( "usage: %s [options] <source file>\n", PROGNAME );
247     printf( "Options are as follows:\n" );
248     printf( "  -h   show this help text\n" );
249     printf
250         ( "  -i   specify header item (repeat to include multiple items)\n" );
251     printf( "  -l   specify source code language (default C/C++)\n" );
252     printf
253         ( "       Supported options are: fortran, fortran90, script, and tex.\n" );
254     printf( "  -p   specify project name\n" );
255     printf( "  -s   include SOURCE item\n" );
256     printf( "  -t   specify CVS/RCS tag to be inserted into a file\n" );
257     printf( "  -x   specify path to ctags binary\n" );
258     printf( "NOTE: requires Exuberant Ctags 5.3.1 (or newer)\n" );
259     printf( "EXAMPLES:\n" );
260     printf
261         ( "robohdrs -s -p myproj -i \"MODIFICATION HISTORY\" -i IDEAS test.c\n" );
262     printf
263         ( "robohdrs -s -p myproj -l script -t '%cHeader:%c' test.tcl\n", '$', '$' );
264     exit( 1 );
265 }
266
267 /********** usage */
268
269 /****f* ROBOhdrs/cmdLine
270 *  NAME
271 *    cmdLine
272 *  SYNOPSIS
273 *    static void cmdLine(int argc, char **argv)
274 *  SOURCE
275 */
276 static void
277 cmdLine( int argc, char **argv )
278 {
279     int                 ch;
280     custhdr_t          *c, *nc;
281
282     while ( ( ch = getopt( argc, argv, "i:l:p:st:x:" ) ) != -1 )
283         switch ( ch )
284         {
285
286         case 's':
287             /* include source item */
288             incSrc = 1;
289             break;
290
291         case 't':
292             /* specify version control tag */
293             strncpy( vcTag, optarg, MAXNAME );
294             break;
295
296         case 'p':
297             /* specify project name */
298             strncpy( projName, optarg, MAXNAME );
299             break;
300
301         case 'i':
302             if ( !custhdrs )
303             {
304                 assert( ( custhdrs =
305                           ( custhdr_t * ) malloc( sizeof( custhdr_t ) ) ) );
306                 custhdrs->next = 0;
307                 c = custhdrs;
308             }
309             else
310             {
311                 c = custhdrs;
312                 while ( c->next )
313                 {
314                     c = c->next;
315                 }
316                 assert( ( nc =
317                           ( custhdr_t * ) malloc( sizeof( custhdr_t ) ) ) );
318                 nc->next = 0;
319                 c = c->next = nc;
320             }
321             strncpy( c->name, optarg, MAXNAME );
322             break;
323
324         case 'l':
325             if ( optarg[0] == 's' && strcmp( optarg, "script" ) == 0 )
326             {
327                 srcSta = SRC_SCRIPT;
328                 srcRem = SRC_R_SCRIPT;
329                 srcEnd = SRC_E_SCRIPT;
330             }
331             else if ( optarg[0] == 'f' && strcmp( optarg, "fortran" ) == 0 )
332             {
333                 srcSta = SRC_FORTRAN;
334                 srcRem = SRC_R_FORTRAN;
335                 srcEnd = SRC_E_FORTRAN;
336             }
337             else if ( optarg[0] == 'f' && strcmp( optarg, "fortran90" ) == 0 )
338             {
339                 srcSta = SRC_F90;
340                 srcRem = SRC_R_F90;
341                 srcEnd = SRC_E_F90;
342             }
343             else if ( optarg[0] == 't' && strcmp( optarg, "tex" ) == 0 )
344             {
345                 srcSta = SRC_TEX;
346                 srcRem = SRC_R_TEX;
347                 srcEnd = SRC_E_TEX;
348             }
349             else if ( optarg[0] == 'a' && strcmp( optarg, "acm" ) == 0 )
350             {
351                 srcSta = SRC_ACM;
352                 srcRem = SRC_R_ACM;
353                 srcEnd = SRC_E_ACM;
354             }
355             else
356             {
357                 usage(  );
358             }
359             break;
360
361         case 'x':
362             strncpy( ctagsBin, optarg, MAXNAME );
363             break;
364
365         case '?':
366         case 'h':
367         default:
368             usage(  );
369             break;
370         }
371 }
372
373 /********** cmdLine */
374 /****f* ROBOhdrs/linenumCompare
375 *  NAME
376 *    linenumCompare
377 *  SYNOPSIS
378 *    static int linenumCompare(void const * a, void const * b)
379 *  SOURCE
380 */
381 static int
382 linenumCompare( void const *a, void const *b )
383 {
384     ctag_t             *ea = ( ctag_t * ) a, *eb = ( ctag_t * ) b;
385
386     return ( ea->linenum - eb->linenum );
387 }
388
389 /********** linenumCompare */
390 /****f* ROBOhdrs/arrangeCtags
391 *  NAME
392 *    arrangeCtags
393 *  SYNOPSIS
394 *    static void arrangeCtags(ctags_t *e)
395 *  SOURCE
396 */
397 static void
398 arrangeCtags( ctags_t * e )
399 {
400     ctag_t             *tmp, *ctag = e->ctag, *ep;
401
402     assert( e && e->cnt && e->ctag );
403     tmp = ( ctag_t * ) malloc( e->cnt * sizeof( ctag_t ) );
404     assert( tmp );
405
406     for ( ep = tmp;; )
407     {
408         memcpy( ep++, ctag, sizeof( ctag_t ) );
409         if ( ctag->next )
410         {
411             ctag = ctag->next;
412         }
413         else
414         {
415             break;
416         }
417     }
418
419     qsort( tmp, ( size_t ) ( e->cnt ), sizeof( ctag_t ), linenumCompare );
420     /* TODO: free ctag */
421     e->ctag = tmp;
422 }
423
424 /********** arrangeCtags */
425 /****f* ROBOhdrs/typeOk
426 *  NAME
427 *    typeOk
428 *  SYNOPSIS
429 *    static int typeOk(char *t)
430 *  SOURCE
431 */
432 static int
433 typeOk( char *t )
434 {
435     /* return 1 if supported type for headers */
436     if ( t[0] == 'f' && strncmp( t, "function", 8 ) == 0 )
437     {
438         return 1;
439     }
440     else if ( t[0] == 'v' && strncmp( t, "variable", 8 ) == 0 )
441     {
442         return 1;               /* global variable */
443     }
444 #if 0
445     else if ( t[0] == 's' && strncmp( t, "struct", 6 ) == 0 )
446     {
447         return 1;
448     }
449 #endif
450     else if ( t[0] == 'm' && strncmp( t, "macro", 5 ) == 0 )
451     {
452         return 1;
453     }
454     return 0;
455 }
456
457
458 /********** typeOk */
459 /****f* ROBOhdrs/initMe
460 *  NAME
461 *    initMe
462 *  SYNOPSIS
463 *    static void initMe(void)
464 *  SOURCE
465 */
466 static void
467 initMe( void )
468 {
469     ctags = &myctags;
470     memset( ctags, 0, sizeof( ctags_t ) );
471     projName[0] = '\0';
472     ctagsBin[0] = '\0';
473     vcTag[0] = '\0';
474 }
475
476 /********** initMe */
477
478 /****f* ROBOhdrs/parseCtagsXLine
479 *  NAME
480 *    parseCtagsXLine
481 *  SYNOPSIS
482 *    static int parseCtagsXLine(char *buf, char *fname, char *name, char *decl, char *type, int *linenum)
483 *  SOURCE
484 */
485 static int
486 parseCtagsXLine( char *buf, char *fname, char *name, char *decl, char *type,
487                  int *linenum )
488 {
489     char               *t, *s;
490
491     /* ctags -x output is: */
492     /* usage            function     56 test.c           void usage(void) */
493     sscanf( buf, "%s%s%d%s", name, type, linenum, fname );
494     s = strstr( buf, fname );
495     while ( *s++ != ' ' )
496     {
497     }
498     while ( *s == ' ' )
499     {
500         ++s;
501     }
502     t = decl;
503     while ( ( *t = *s++ ) != '\n' )
504     {
505         ++t;
506     }
507     *t = '\0';
508
509     return 0;
510 }
511
512 /********** parseCtagsXLine */
513
514 /****f* ROBOhdrs/addList
515 *  NAME
516 *    addList
517 *  SYNOPSIS
518 *    static void addList(ctags_t *e, char *fname, char *name, char *decl, char *type, int linenum)
519 *  SOURCE
520 */
521 static void
522 addList( ctags_t * e, char *fname, char *name, char *decl, char *type,
523          int linenum )
524 {
525     ctag_t             *newctag, *ctag = e->ctag;
526
527     if ( !ctag )
528     {
529         /* empty list */
530         ctag = ( ctag_t * ) malloc( sizeof( ctag_t ) );
531         assert( ctag );
532         memset( ctag, 0, sizeof( ctag_t ) );
533         e->ctag = ctag;
534     }
535     else
536     {
537         while ( ctag->next )
538         {
539             ctag = ctag->next;
540         }
541         newctag = ( ctag_t * ) malloc( sizeof( ctag_t ) );
542         assert( newctag );
543         memset( newctag, 0, sizeof( ctag_t ) );
544         ctag->next = newctag;
545         newctag->prev = ctag;
546         ctag = newctag;
547     }
548
549     e->cnt++;
550
551     strncpy( ctag->fname, fname, MAXNAME );
552     strncpy( ctag->name, name, MAXNAME );
553     strncpy( ctag->decl, decl, MAXLINE );
554     strncpy( ctag->type, type, MAXNAME );
555     ctag->linenum = linenum;
556 }
557
558 /********** addList */
559 /****f* ROBOhdrs/parseCtagsX
560 *  NAME
561 *    parseCtagsX
562 *  SYNOPSIS
563 *    static int parseCtagsX(FILE *fp)
564 *  SOURCE
565 */
566 static int
567 parseCtagsX( FILE * fp )
568 {
569     char                buf[MAXLINE + 1];
570     int                 lnum = 0, tagsParsed = 0;
571
572     while ( fgets( buf, MAXLINE, fp ) != NULL )
573     {
574         char                decl[MAXNAME + 1], name[MAXNAME + 1];
575         char                fname[MAXNAME + 1], type[MAXNAME + 1];
576         int                 linenum;
577
578         lnum++;
579         /* extract info from a line */
580         if ( parseCtagsXLine( buf, fname, name, decl, type, &linenum ) )
581         {
582             printf( "error parsing line (%d)", lnum );
583         }
584         else
585         {
586             addList( ctags, fname, name, decl, type, linenum );
587             tagsParsed++;
588         }
589     }                           /* end while() */
590     fclose( fp );
591
592     return tagsParsed;
593 }
594
595 /********** parseCtagsX */
596
597 /****f* ROBOhdrs/roboFileHeader
598 *  NAME
599 *    roboFileHeader
600 *  FUNCTION
601 *    Insert source file header.
602 *  SYNOPSIS
603 *    static void roboFileHeader(FILE *fp, char *proj, char *fname)
604 *  SOURCE
605 */
606 static void
607 roboFileHeader( FILE * fp, char *proj, char *fname )
608 {
609     char               *s;
610
611     s = remark_markers[srcRem];
612     fprintf( fp, "%sh* %s/%s\n", header_markers[srcSta],
613              ( proj[0] ? proj : fname ), fname );
614     fprintf( fp, "%s  NAME\n", s );
615     fprintf( fp, "%s    %s\n", s, fname );
616     if ( *vcTag )
617     {
618         fprintf( fp, "%s    %s\n", s, vcTag );
619     }
620     fprintf( fp, "%s  DESCRIPTION\n", s );
621     fprintf( fp, "%s*******%s\n", s,
622              ( end_remark_markers[srcEnd] ? end_remark_markers[srcEnd] :
623                "" ) );
624 }
625
626 /********** roboFileHeader */
627
628 /****f* ROBOhdrs/roboHeader
629 *  NAME
630 *    roboHeader
631 *  SYNOPSIS
632 *    static void roboHeader(FILE *fp, char *fname, char *name, char *type, char *decl)
633 *  SOURCE
634 */
635 static void
636 roboHeader( FILE * fp, char *fname, char *name, char *type, char *decl )
637 {
638     custhdr_t          *c;
639     char               *s;
640
641     s = remark_markers[srcRem];
642     if ( type[0] == 'v' )
643     {
644         fprintf( fp, "%sv* %s/%s\n", header_markers[srcSta], fname, name );
645     }
646     else if ( type[0] == 'm' )
647     {
648         fprintf( fp, "%sd* %s/%s\n", header_markers[srcSta], fname, name );
649     }
650 #if 0
651     else if ( type[0] == 's' )
652     {
653         fprintf( fp, "/****s* %s/%s\n", fname, name );
654     }
655 #endif
656     else
657     {
658         fprintf( fp, "%sf* %s/%s\n", header_markers[srcSta], fname, name );
659     }
660
661     fprintf( fp, "%s  NAME\n%s    %s\n", s, s, name );
662     if ( type[0] != 'm' )
663     {
664         fprintf( fp, "%s  SYNOPSIS\n%s    %s\n", s, s, decl );
665     }
666     if ( custhdrs )
667     {
668         for ( c = custhdrs;; )
669         {
670             fprintf( fp, "%s  %s\n", s, c->name );
671             if ( c->next )
672             {
673                 c = c->next;
674             }
675             else
676             {
677                 break;
678             }
679         }
680     }
681
682     if ( incSrc )
683     {
684         fprintf( fp, "%s  SOURCE\n%s\n", s,
685                  ( end_remark_markers[srcSta] ? end_remark_markers[srcSta] :
686                    s ) );
687     }
688     else
689     {
690         fprintf( fp, "%s***%s\n", s,
691                  ( end_remark_markers[srcSta] ? end_remark_markers[srcSta] :
692                    "" ) );
693     }
694 }
695
696 /********** roboHeader */
697
698 /****f* ROBOhdrs/insertSrcEnd
699 *  NAME
700 *    insertSrcEnd
701 *  SYNOPSIS
702 *    static void insertSrcEnd(FILE *fp, char *funcname)
703 *  SOURCE
704 */
705
706 static void
707 insertSrcEnd( FILE * fp, char *funcname )
708 {
709     fprintf( fp, "%s********* %s %s\n", end_markers[srcEnd], funcname,
710              ( end_remark_markers[srcSta] ? end_remark_markers[srcSta] :
711                "" ) );
712 }
713
714 /********** insertSrcEnd */
715
716 /****f* ROBOhdrs/insertHeaders
717 *  NAME
718 *    insertHeaders
719 *  SYNOPSIS
720 *    static void insertHeaders(ctags_t *e, char *project, char *dstpath, char *srcpath)
721 *  SOURCE
722 */
723 static void
724 insertHeaders( ctags_t * e, char *project, char *dstpath, char *srcpath )
725 {
726     FILE               *ifp, *ofp;
727     ctag_t             *ctag = e->ctag;
728     int                 lnum = 0, funcline = 0;
729     char                buf[MAXLINE], *funcname = 0;
730
731     if ( !ctag || !dstpath || !srcpath )
732     {
733         return;
734     }
735
736     assert( ofp = fopen( dstpath, "w" ) );
737     assert( ifp = fopen( srcpath, "r" ) );
738
739     /* include file header only if project name is defined */
740     if ( project )
741     {
742         roboFileHeader( ofp, project, dstpath );
743     }
744
745     while ( fgets( buf, MAXLINE, ifp ) != NULL )
746     {
747         lnum++;
748         while ( ctag->prev )
749         {
750             ctag = ctag->prev;
751         }
752         for ( ;; )
753         {
754             if ( incSrc && funcline && lnum >= funcline
755                  && ctag->linenum == lnum )
756             {
757                 funcline = 0;
758                 insertSrcEnd( ofp, funcname );
759             }
760             if ( ctag->linenum == lnum )
761             {
762                 if ( typeOk( ctag->type ) )
763                 {
764                     roboHeader( ofp, ctag->fname, ctag->name, ctag->type,
765                                 ctag->decl );
766                     funcline = lnum;
767                     funcname = ctag->name;
768                 }
769                 break;
770             }
771             else if ( ctag->next )
772             {
773                 ctag = ctag->next;
774             }
775             else
776             {
777                 break;
778             }
779         }                       /* end ctag loop */
780         fprintf( ofp, "%s", buf );
781     }
782
783     if ( incSrc && funcline )
784     {
785         insertSrcEnd( ofp, funcname );
786     }
787
788     fclose( ifp );
789     fclose( ofp );
790 }
791
792 /********** insertHeaders */
793 /****f* ROBOhdrs/doCtagsExec
794 *  NAME
795 *    doCtagsExec
796 *  SYNOPSIS
797 *    static FILE * doCtagsExec(char *fname)
798 *  SOURCE
799 */
800 static FILE        *
801 doCtagsExec( char *fname )
802 {
803     int                 fd[2], pid;
804     FILE               *incoming;
805     char               *mybin, *bin = "ctags";
806
807     mybin = ( ctagsBin[0] ? ctagsBin : bin );
808
809     if ( pipe( fd ) == -1 )
810     {
811         fprintf( stderr, "pipe failed\n" );
812         exit( 1 );
813     }
814
815     if ( ( pid = fork(  ) ) == 0 )
816     {
817         close( 1 );
818         dup( fd[1] );
819         close( fd[0] );
820         if ( execlp( mybin, mybin, "-x", fname, NULL ) == -1 )
821         {
822             fprintf( stderr, "execlp failed\n" );
823             exit( 1 );
824         }
825     }
826     else if ( pid == -1 )
827     {
828         fprintf( stderr, "fork failed\n" );
829         exit( 1 );
830     }
831     else
832     {
833         close( 0 );
834         dup( fd[0] );
835         close( fd[1] );
836
837         if ( ( incoming = fdopen( 0, "r" ) ) == NULL )
838         {
839             fprintf( stderr, "fdopen failed\n" );
840             exit( 1 );
841         }
842     }
843
844     return incoming;
845 }
846
847 /********** doCtagsExec */
848 /****f* ROBOhdrs/doFile
849 *  NAME
850 *    doFile
851 *  SYNOPSIS
852 *    static void doFile(char *proj, char *fname)
853 *  SOURCE
854 */
855 static void
856 doFile( char *proj, char *fname )
857 {
858     char                buf[MAXLINE];
859
860     /* backup */
861     sprintf( buf, "/bin/cp -p %s %s~", fname, fname );
862     system( buf );
863
864     if ( parseCtagsX( doCtagsExec( fname ) ) < 1 )
865     {
866         fprintf( stderr, "no tags\n" );
867         exit( 1 );
868     }
869
870     arrangeCtags( ctags );
871     sprintf( buf, "%s~", fname );
872     insertHeaders( ctags, proj, fname, buf );
873 }
874
875 /********** doFile */
876 /****f* ROBOhdrs/cleanUp
877 *  NAME
878 *    cleanUp
879 *  SYNOPSIS
880 *    static void cleanUp(void)
881 *  SOURCE
882 */
883 static void
884 cleanUp( void )
885 {
886
887 }
888
889 /********** cleanUp */
890
891
892 /****f* ROBOhdrs/main
893 *  NAME
894 *    main
895 *  SYNOPSIS
896 *    int main(int argc, char **argv)
897 *  SOURCE
898 */
899 int
900 main( int argc, char **argv )
901 {
902     initMe(  );
903     cmdLine( argc, argv );
904     argc -= optind;
905     argv += optind;
906     if ( argc == 1 )
907     {
908         doFile( projName, argv[0] );
909     }
910     else
911     {
912         usage(  );
913     }
914     cleanUp(  );
915     return 0;
916 }
917
918 /********** main */
919
920
921 #endif /*  HAVE_FORK  */
922