Subversion Repositories doc-tools

Rev

Rev 3 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
3 axl 1
#!/usr/bin/perl
2
 
3
#  
4
#  File: php_autodoc_fornd.pl
5
#  Crée une "coquille documentaire" pour Natural Docs dans les fichiers PHP.
6
#
7
#  Bien entendu, cela doit être ensuite complété à la main
8
#
4 axl 9
#  Version : 0.6
3 axl 10
#
11
#  Language: Perl
12
#
13
#  Svn info: Résumé
14
#  $Id: php_autodoc_fornd.pl 4 2007-05-31 23:49:44Z axl $
15
#
16
#  Svn info: Révision
17
#  $Revision: 4 $
18
#
19
#  Svn info: Auteur de la révision
20
#  $Author: axl $
21
#
22
 
23
#
24
#  variable: $php_name
25
#  valeur : PHP
26
# 
27
#  variable: $js_name
28
#  valeur : javascript
29
# 
30
#  variable: $perl_name
31
#  valeur : Perl
32
# 
33
$php_name='PHP';
34
$js_name='javascript';
35
$perl_name='Perl';
36
 
37
#use strict;
38
 
39
# Pour la gestion des options
40
use Getopt::Std;
41
# Pour la copie de fichiers
42
use File::Copy;
43
 
44
#  
45
#  Function: print_syntaxe
46
#
47
#  Affiche la syntaxe d'utilisation
48
#  
49
#  Retourne:
50
#
51
#  rien
52
#
53
 
54
sub print_syntaxe {
55
  # Chemin complet du script Perl
56
  my $NomScript = $0;
57
  # Nom du script seulement
58
  $NomScript =~ s!.*(/|\\)!!;
59
  print <<_FINSY_;
60
 
61
Syntaxe :
62
  $NomScript -i nom_fichier [-option1 [parametres]] [-option2 ...]
63
  $NomScript -h
64
 
65
Description des options :
66
  -i nom_fichier
67
     nom_fichier nom du (des) fichier(s)) d\'input (accepte les jokers)
68
  -x fichiers_exclus
69
     fichiers_exclus est un masque de fichiers exclus du traitement
70
     note : les fichiers .bak sont toujours exclus (option -x inutile pour eux)
71
  -d {n} n niveau de déboguage (1 seulement pour l\'instant)
72
  -e (except) est suivi de la liste des fonctions exclues du traitement
73
  -o (only) est suivi de la liste des seules fonctions incluses
74
     dans le traitement
75
     -e et -o sont incompatibles ; -o est prioritaire
76
     les mots de la liste sont separes par des virgules, points-virgules
77
     ou des espaces (dès que la liste contient des espaces, elle doit être
78
     entre doubles quotes).
79
     Les fonctions existantes actuellement sont :
80
     - constant
81
     - en_tete
82
     - function (tous langages confondus)
83
     - js_func  (fonctions javascript)
84
     - include
85
  -h afficher cette aide
86
 
87
Exemples :
88
  $NomScript -i *.php -x *.old  
89
  $NomScript -i *.php -x *_ND.php  
90
  $NomScript -i *.php -e include,js_func,constant  
91
  $NomScript -i *.php -o "en_tete function"
92
_FINSY_
93
  exit;
94
  }
95
 
96
#  
97
#  Function: output_en_tete
98
#
99
#  Ajoute les généralités concernant le fichier
100
#  
101
#  Parameters:
102
#  $O : Handle du fichier de sortie
103
#
104
#  $entree : Nom du fichier en entrée
105
#
106
#  Retourne:
107
#
108
#  rien
109
#  
110
 
111
sub output_en_tete {
112
  # Si désactivé retour
113
    if ($feature{'en_tete'}==0) {
114
        return;
115
        }
116
 
117
  # Recup des paramètres
118
  my ($O, $entree) = @_;
119
 
120
  # Séparer répertoire et nom de fichier
121
  if ($entree=~m!^(.*)(\\|/)(.*)$!) {
122
    $repertoire=$1;
123
    # Si répertoire est '.' alors on vide pour l'affichage
124
    #if ($repertoire eq '.') {
125
    #  $repertoire='';
126
    #  }
127
    $fichier=$3;
128
    }
129
  else {
130
    $repertoire='.';
131
    $fichier=$entree;
132
    }
133
 
134
  # En tête de fichier pour Natural Docs
135
  if ($fichier=~/\.php3?$/) {
136
    $langage = "\n  language: $php_name\n";  
137
    }
4 axl 138
  elsif ($fichier=~/\.js$/) {
139
    $langage = "\n  language: $js_name\n";
140
    $language_context=$js_name;
141
    push(@language_context_stack, $language_context);
142
    }
3 axl 143
  elsif ($fichier=~/\.p(l|m)$/) {
144
    $langage = "\n  language: $perl_name\n";
145
    }  
146
  else {
147
    $langage = "\n";
148
    }
149
  $EnTete = <<_FIN_;
150
<?
151
/*
152
  file: $fichier
153
  Rôle du script...
154
 
155
  Repertoire: $repertoire
156
    $repertoire/
157
$langage
158
  svn info:  Résumé
159
    \$Id\$
160
 
161
  Svn info: Révision
162
  \$Revision\$
163
 
164
  Svn info: Auteur de la révision
165
  \$Author\$
166
*/
167
_FIN_
168
 
169
  $EnTete.="?>";
170
  print $O $EnTete;
171
  }
172
 
173
#  
174
#  Function: output_parameters
175
#
176
#  Ajoute les paramètres de la fonction (un par
177
#  ligne, séparés par une ligne vide)
178
#  
179
#  Retourne:
180
#
181
#  rien
182
#  
183
 
184
sub output_parameters {
185
  my ($param) = @_;
186
  print O "\n  Parameters:\n";
187
  # Sort les paramètres un par un pour commentaires manuels futurs
188
  while ($param=~s/^(\$[a-zA-Z_0-9]+)( *, *(.*))?$/$3/) {
189
    print O "  $1 :\n\n";
190
    }
191
  }
192
 
193
#  
194
#  Function: output_function
195
#
196
#  Ajoute le commentaire de fonction
197
#  
198
#  Retourne:
199
#
200
#  rien
201
#  
202
 
203
sub output_function {
204
    # Si désactivé retour quel que soit le langage en cours
205
    if ($feature{'function'}==0) {
206
        return;
207
        }
208
    # Si désactivé pour javascript et javascript en cours, retour
209
    if ( ($language_context eq $js_name) and ($feature{'js_func'}==0) ) {
210
        return;
211
        }
212
 
213
    my ($func_name,$param_liste)=@_;
214
    print O "/*\n  Function: $func_name\n";
4 axl 215
    print O "\n  Language:\n  $language_context\n";
3 axl 216
    output_parameters($param_liste);
217
    print O "\n*/\n";
218
    }
219
 
220
#  
221
#  Function: output_constant
222
#
223
#  Ajoute la constante dont le nom et la valeur
224
#  ont été passés en paramètres
225
#  
226
#  Parameters:
227
#  $ConstName : Nom de la constante
228
#
229
#  $ConstValue : Valeur de la constante
230
#
231
#  Retourne:
232
#
233
#  rien
234
#  
235
 
236
sub output_constant {
237
  # Si désactivé retour
238
    if ($feature{'constant'}==0) {
239
        return;
240
        }
241
 
242
  my ($ConstName,$ConstValue) = @_;
243
    print O "/*\n  Constant: $ConstName\n\n  $ConstValue\n";
244
    print O "\n  Signification: ...\n*/\n";
245
  }
246
 
247
#  
248
#  Function: output_include
249
#
250
#  Ajoute la déclaration d'inclusion passée en paramètre
251
#  
252
#  Parameters:
253
#  $IncludeWord : commande d'inclusion
254
#
255
#  $IncludeFile : Fichier inclus (peut être une formule)
256
#
257
#  Retourne:
258
#
259
#  rien
260
#  
261
 
262
sub output_include {
263
  # Si désactivé retour
264
    if ($feature{'include'}==0) {
265
        return;
266
        }
267
 
268
  my ($IncludeWord,$IncludeFile) = @_;
269
    print O "/*\n  Inclusion: $IncludeWord\n\n";
270
    print O "  $IncludeFile\n*/\n";
271
  }
272
 
273
#  
274
#  Function: try_language_context
275
#
276
#  Essai de repérer le langage de programmation courant.
277
#  Suppose l'absence de changement au sein d'une ligne ce qui est une
278
#  simplification abusive, on le sait.
279
#
280
#  Toutes les regexp de ce script sont de vraies passoires laissant
281
#  délibérément de côté tout un tas de cas (par manque de temps par
282
#  rapport aux enjeux)
283
#
284
#  Modifie:
285
#
286
#  Les variables globales $language_context et @language_context_stack
287
#  
288
#  Retourne:
289
#
290
#  rien
291
#  
292
 
293
sub try_language_context {
294
 
295
    # Ignorer les courtes inclusions de PHP au sein du HTML
296
    # ou du javascript
297
    if (/^ *<\?(php)?.+\?>/) {
298
        return;
299
        }
300
    # Début PHP ?
301
    if (/^ *<\?(php)?/i) {
302
        push(@language_context_stack, $language_context);    
303
        $language_context=$php_name;
304
        if ($debug_level eq 1) {
305
            print "$line_number:dp:#$_ current : $language_context\n";
306
            print @language_context_stack; print "\n";
307
            }
308
        }
309
    # javascript sur une seule ligne -> on ne change rien   
310
    if (m:^ *<(!--)?SCRIPT +LANGUAGE=.?javascript.?>.*</script(--)?>:i) {    
311
        # On ne fait rien en l'état actuel du développement
312
        }
313
    # Sinon début javascript    
314
    else {
315
        if (/^ *<(!--)?SCRIPT +LANGUAGE=.?javascript.?>/i) {
316
            push(@language_context_stack, $language_context);    
317
            $language_context=$js_name;
318
            if ($debug_level eq 1) {
319
                print "$line_number:dj:#$_ current : $language_context\n";
320
                print @language_context_stack; print "\n";
321
                }
322
            return;
323
            }
324
        }
325
    # Fin javascript ?  
326
    if (m:</script(--)?>:i) {    
327
        $language_context=pop(@language_context_stack);
328
        if ($debug_level eq 1) {
329
            print "$line_number:fj:#$_ current : $language_context\n";
330
            print @language_context_stack; print "\n";
331
            }
332
        }
333
    # Fin PHP ?  (Hum regexp bidon on peut avoir un "<" qui ne soit pas
334
    # un "<?")
335
    if ((/\?>.*$/) and ($language_context eq $php_name)) {
336
        my $ligne=$_;
337
        while ($ligne=~/\?>.*<\?/) {
338
            $ligne=~s/^.*\?>.*<\?(.*)$/$1/;
339
            }
340
        if ($ligne=~/\?>.*$/) {
341
            $language_context=pop(@language_context_stack);
342
            if ($debug_level eq 1) {
343
                print "$line_number:fp:#$_ current : $language_context\n";
344
                print @language_context_stack; print "\n";
345
                }
346
            }  
347
        }
348
    }
349
 
350
#  
351
#  Function: try_to_skip_svnid
352
#
353
#  Efface le mot-clé Id de subversion s'il existe déjà dans le fichier
354
#  
355
#  Retourne:
356
#
357
#  Un booléen
358
#
359
#  - true : il n'y plus rien d'autre sur la ligne qu'un signe de début 
360
#    de commentaire ligne et des espaces
361
#  - il reste des éléments d'information sur la ligne
362
#  
363
 
364
sub try_to_skip_svnid {
365
  if ($_=~/\$Id.*\$/) {
366
    $_=~s!^(.*?)\$Id[^\$]*\$(.*)$!$1$2!;
367
    if ($_=~/^(\/\/|#) *$/) {return 1;}
368
    }
369
  return 0;
370
  }
371
 
372
#  
373
#  Function: try_function_begin
374
#
375
#  Teste si une fonction débute et si oui traite en conséquence
376
#  
377
#  Parameters:
378
#
379
#  aucun
380
#
381
#  Retourne:
382
#
383
#  vrai (1) si début de fonction trouvé et faux (0) sinon
384
#   
385
 
386
sub try_function_begin {
387
    if ($_=~/^ *function *([a-zA-Z_0-9]+)\(([^)]*)(.*)$/i) {
388
        output_function($1,$2);
389
        print O $_;
390
        return 1;
391
        }
392
    else {
393
        return 0;
394
        }
395
    }
396
 
397
#  
398
#  Function: try_constant_define
399
#
400
#  Teste si une définition de constante est sur la ligne.
401
#  Si oui, traite en conséquence.
402
#  
403
#  Parameters:
404
#
405
#  aucun
406
#
407
#  Retourne:
408
#
409
#  vrai (1) si définition de constante trouvée et faux (0) sinon
410
#   
411
 
412
sub try_constant_define {
413
    # Attention aux références arrières \1 et \3 qu permettent de s'assurer
414
    # que la paire de quote (simple ou double) est cohérente
415
    if ($_=~/^ *define *\( *(['"])([a-zA-Z_0-9]+)\1 *, *(["']?)(.*?)\3 *\)/) {
416
        output_constant($2,$4);
417
        print O $_;
418
        return 1;
419
        }  
420
    else {
421
        return 0;
422
        }
423
    }
424
 
425
#  
426
#  Function: try_include_def
427
#
428
#  Teste si une inclusion de fichier est sur la ligne.
429
#  Si oui, traite en conséquence.
430
#  
431
#  Parameters:
432
#
433
#  aucun
434
#
435
#  Retourne:
436
#
437
#  vrai (1) si définition de constante trouvée et faux (0) sinon
438
#   
439
 
440
sub try_include_def {
441
    # Trouver le mot-clé
442
    my $regexp='^ *(include|require)(_once)? *\\(';
443
 
444
    # Trouver la succession d'éléments constants et littéraux
445
# (pas traité éléments variables : à faire)
446
    $regexp.="((($TYPE2_FILE_PATTERN|$LAXIST_CONST_PATTERN)\\.?)+)";
447
 
448
    # Fermer la regexp par la reconnaissance de la fin de parenthèse
449
    $regexp.=' *\\)';
450
 
451
    if ($_=~/$regexp/) {
452
        output_include($1.$2,$3);
453
        print O $_;
454
        return 1;
455
        }  
456
    else {
457
        return 0;
458
        }
459
    }
460
 
461
#  
4 axl 462
#  Function: backup_file
463
#
464
#  Traite un fichier en entrée en lui ajoutant les commentaires
465
#  
466
#  Parameters:
467
#  $inputFile : chemin d'accès au fichier
468
#
469
#  Retourne:
470
#
471
#  rien
472
#  
473
 
474
sub backup_file {
475
    # Récupération chemin de tavail et nom de fichier
476
    my ($inputFile) = @_;
477
    # Supprimer le "./" initial s'il existe ; récupérer path et fichier
478
#print "$inputFile\n";
479
    $inputFile=~s!^(\./)?((.*)/)?(.*)$!$3/$4!;
480
    my $rep=$3;
481
    my $file=$4;
482
#print "$inputFile\n$rep#$file\n";
483
    # Vérification de l'existence des répertoires du chemin de backup
484
    # On teste d'abord le chemin complet pour aller vite
485
    # S'il existe, on passe directement à la copie
486
    # Sinon on le traite élément par élément de gauche à droite
487
    my $full_dir_path="ND_backup_dir/$rep";
488
    if (!-d $full_dir_path) {
489
        @path_element=split('/',$full_dir_path);
490
        my $current_path='.';
491
        foreach my $element (@path_element ) {
492
            $current_path="$current_path/$element";
493
            if (!-d $current_path) {
494
                # créer le rep;
495
                if (! mkdir $current_path,0660) {
496
                    print "\n$current_path : erreur creation !";
497
                    }
498
                }
499
            }
500
        }
501
 
502
    # Calcul du chemin du fichier de backup
503
    my $backupFile="ND_backup_dir/$rep/$file";
504
    $backupFile.='.bak';
505
    # Copie de l'original vers le répertoire de backup
506
    copy($inputFile,$backupFile);
507
    }
508
 
509
#  
3 axl 510
#  Function: process_one_file
511
#
512
#  Traite un fichier en entrée en lui ajoutant les commentaires
513
#  
514
#  Parameters:
515
#  $rep : chemin de travail (sans le slash final)
516
#
517
#  $entree : Nom du fichier
518
#
519
#  Retourne:
520
#
521
#  rien
522
#  
523
 
524
sub process_one_file {
525
  $line_number=0;
526
  # Récupération chemin de tavail et nom de fichier
527
  my ($rep,$entree) = @_;
528
  # Initialisation de variable
529
  my $langage = "";
530
 
531
  # Calcul nom du fichier de sortie selon qu'il ait une extension ou pas
532
  if ($entree=~m/^(.*)\.(.*)$/) {
533
    $sortie=$rep."/$1_ND.$2";
534
    }
535
  else {
536
    $sortie=$rep."/$1_ND";
537
    }
538
 
539
  # Calcul du chemin du fichier d'entrée
540
  $inputFile="$rep/$entree";  
541
  # Affichage écran du travail en cours
542
  print "Traitement de $entree vers $sortie\n";
543
 
544
  # Faire copie de sauvegarde
4 axl 545
  backup_file($inputFile);
3 axl 546
 
547
  # Ouvrir les fichiers d'entrée et de sortie
548
  open F,"$inputFile";
549
  open O,">$sortie";
550
 
551
  # Eliminer l'éventuel "./" du début de chemin pour les affichages
552
  $inputFile=~s/^(.\/)?(.*)/$2/;
553
 
554
  # Ajouter l'en-tête fichier pour Natural Docs
555
  output_en_tete(O, $inputFile);
556
 
557
  # Traitement du fichier proprement dit  
558
  while (<F>) {
559
    $line_number+=1;
560
    #print "$_\n";
561
    # Mettre à jour le contexte de langage
562
    try_language_context();
563
    # Enlever le Id subversion si présent (car ajouté dans en-tête)
564
    if (try_to_skip_svnid()) {
565
      next;
566
      }
567
    # Trouver, traiter les fonctions  
568
    if (try_function_begin()) {
569
      next;
570
      }    
571
    # Trouver, traiter les définition de constantes  
572
    if (try_constant_define()) {
573
      next;
574
      }    
575
    # Trouver, traiter les inclusions  
576
    if (try_include_def()) {
577
      next;
578
      }    
579
    # Ecrit la ligne courante dans le fichier de sortie
580
    print O $_;      
581
    }    
582
  close F;
583
  close O;
584
  }
585
 
586
#  
587
#  Function: process_many_files
588
#
589
#  Traite de multiples fichiers en entrée en leur ajoutant les commentaires
590
#  
591
#  Parameters:
592
#  $masque : répertoire de travail et masque des fichiers à traiter
593
#
594
#  $x_masque : masque de fichiers à exclure
595
#  (les fichier .bak sont toujours exclus quoiqu'il arrive)
596
#
597
#  Retourne:
598
#
599
#  rien
600
#  
601
 
602
sub process_many_files {
603
  # Récupérer les masques de fichiers à inclure et à exclure
604
  my ($masque,$x_masque) = @_;
605
 
606
  # Déterminer le répertoire de travail
607
  my $repertoire = $masque;
608
  # Si ni / ni \ alors c'est le répertoire courant
609
  if ($repertoire!~s!(.*)(/|\\)(.*)!$1!) {
610
    $repertoire=".";
611
    }
612
  else {
613
    # sinon récupérer juste le masque de fichiers
614
    $masque=~s!(.*)(/|\\)(.*)!$3!;
615
    }  
616
  # print "\nRepertoire : $repertoire\n"; # debug
617
 
618
  # Transformer les masques de nom de fichiers en regexp
619
  # Protéger les "."
620
  $masque=~s/\./\\./g;
621
  $x_masque=~s/\./\\./g;
622
  # Ajouter un "." devant les quantificateurs "*" et "?"
623
  $masque=~s/\*/.*/g;
624
  $masque=~s/\?/.?/g;
625
  $x_masque=~s/\*/.*/g;
626
  $x_masque=~s/\?/.?/g;
627
 
628
  my $enregistrement;
629
  my $nom_chemin;
630
  local *DH;
631
 
632
  unless (opendir(DH, $repertoire)) {
633
   return;
634
  }
635
 
636
  while (defined ($enregistrement = readdir(DH))) {
637
    next if($enregistrement eq "." or $enregistrement eq "..");
638
    # Passer les .bak
639
    next if($enregistrement=~/\.bak$/);
640
    # Passer les éléments exclus
641
    next if($enregistrement=~/^$x_masque$/);
642
 
643
    $nom_chemin = $repertoire."/".$enregistrement;
644
    if( -d $nom_chemin) {
645
      # print "\n$nom_chemin est un repertoire -> pas de traitement recursif\n";
646
      }
647
    else {
648
      if ($enregistrement=~/^$masque$/) {
649
        #print "\n$nom_chemin -> en cours de traitement"; 
650
        process_one_file($repertoire,$enregistrement);      
651
        }
652
      }
653
    # Plus tard pour le traitement récusrsif  
654
    # push(@tous, $HTML_enregistrement);
655
    # rechercher($nom_chemin) if(-d $nom_chemin);
656
    }
657
  closedir(DH);
658
  }  
659
 
660
#
661
#  Main part of script
662
#
663
 
664
# Récupérer les options de la ligne de commande
665
getopts( "i:x:e:o:d:s:h", \%opts) or print_syntaxe();
666
print_syntaxe() if defined($opt{h});
667
 
668
# Initialisation variables
669
$line_number=0;
670
$language_context='unknown';
671
@language_context_stack = ('stackbase_');
672
$TYPE2_FILE_PATTERN="('[A-Za-z0-9_.\-]+'|\"[A-Za-z0-9_.\-]+\")";
673
$CONST_PATTERN='[A-Z0-9_]+';
674
$LAXIST_CONST_PATTERN='[a-zA-Z0-9_]+';
675
 
676
# traiter les options de la ligne de commande
677
 
678
# récupérer le(s) nom(s) de(s) fichier(s) à traiter et de ceux à exclure
679
my $inputfile = $opts{i};
680
my $exclude_files = $opts{x};
681
 
682
# récupérer les options de travail
683
my $except_features = $opts{e};
684
my $only_features = $opts{o};
685
$debug_level = $opts{d};
686
 
687
# établir la liste des fonctionnalités actives
688
%feature = (
689
             en_tete   => 1,
690
             function  => 1,
691
             js_func => 1,
692
             constant => 1,
693
             include => 1,
694
             exec => 0
695
);
696
 
697
# traiter l'option -e (liste de fonctionnalités exclues du traitement)
698
my @exception_liste = split(/ *[ ,;] */,$except_features);
699
foreach my $val (@exception_liste) {
700
    $feature{$val}=0;
701
    }
702
 
703
# traiter l'option -o (traiter seulement les fonctionnalités listées)
704
if ($only_features ne "") {
705
    # désactiver toutes les fontionnalités
706
    foreach my $key (keys %feature) {
707
        $feature{$key}=0;
708
        }
709
    # réactiver uniquement cells de la liste fournie    
710
    my @restricted_liste = split(/ *[ ,;] */,$only_features);
711
    foreach my $val (@restricted_liste) {
712
        $feature{$val}=1;
713
        }
714
    }
715
 
716
# Si nom de fichier vide afficher syntaxe
717
if ($inputfile eq "") {
718
  print_syntaxe();
719
  }
720
 
721
# Tester le paramètre input (-i) 
722
if ($inputfile=~/\*|\?/) {
723
  process_many_files($inputfile,$exclude_files);
724
  }
725
else {
726
  if (-e $inputfile) {
727
    # Attention BUG en perspective - PROVISOIRE (pas le temps)
728
    # Il faut d'abord séparer chemin du nom de fichier
729
    process_one_file(".",$inputfile);
730
    }
731
  else {
732
    print "\n$inputfile n'existe pas !\n";
733
    print_syntaxe();
734
    }      
735
  }
736
 
737
if ($debug_level eq 1) {
738
    print @language_context_stack;
739
    }
740
 
741
# Fin du script