OVH Cloud OVH Cloud

[shell] Creation d'un fichier de references croisees

16 réponses
Avatar
Sébastien Kirche
Bonjour aux grands gourous du shell :)

Soit un ensemble de fichiers .c d'un projet conséquent (+ de 500).
Certains de ces fichiers appellent un ensemble de fonctions maison dont le
nom commence toujours par la forme "CB_" et dont les arguments comportent
au moins 2 arguments (le premier est un pointeur - on s'en tamponne, mais
le second est un define que j'essaie de récupérer dans mon xref)

Je m'explique un peu plus en détail :
j'essaie de fabriquer un fichier texte contenant la liste des modules c
faisant ces appels l'aide de grep.
Et pour chaque fichier trouvé, j'aimerais la liste des fonctions cb_qqchose
appelées avec leurs paramètres : encore un grep
Et j'aimerais établir une liste du 2ème paramètre de mes fonctions
cb_qqchose avec les liste des fichiers où il se trouve et si possible la
ligne...

Je me débrouille en shell en général, mais je suis débutant en sed/awk qui
me semblent indiqués pour la tâche.

En plus les fichiers sources sont encodés pour mac, ce qui complique encore
un peu la tâche.

J'ai commencé un script shell, mais j'ai déjà un problème pour obtenir les
fonctions cb_qqchose : j'ai des faux positifs...

Voici mon embryon de script qui cafouille déjà:
-------------------------
#! /bin/bash

CFiles=`ls /Volumes/SeKiOS9/User/dev_prj/Sage/Columbia/source/*.c`

for c in $CFiles
do
CBAccesFiles=`grep -l CB_ $c` #| xargs -n 1 basename
for cb in $CBAccesFiles
do
basename $cb
cat $cb | tr '\r' '\n' | grep CB_ | awk '/CB_.*\(/,/\)/ { print $1}'
done
done
-------------------------

Qui donne (extrait):

CaisPrin.c
CB_UnLockRecordVisu(gCIALRef,
FailCbErr(CB_LockFile(gCIALRef,CAISSE,CBLCKEX));
FailCbErr(CB_Acces
if
FailCbErr(CB_GetRecordPos
FailCbErr(CB_Acces
FailCbErr(CB_GetRecordPos
FailCbErr(CB_Notify(gCIALRef,CAISSE,CB_ADD,0,ALLNOTIFY,lPos));
FailCbErr(CB_LockRecordVisu(gCIALRef,CAISSE,DataFen.bModification,lPos));
FailCbErr(CB_SetRecordPos(gCIALRef,gCaisseIndex,lPos,&fCaisse));
FailCbErr(CB_Acces(gCIALRef,EQGREATER,IDO_CAISSE,&Doc));

Je ne comprend pas les lignes "if" ni les "failcberr..." moi je voudrais
juste l'appel CB_LockFile(...) ou CB_Acces(...) --> problème de regexp avec
awk ?

Pour le problème d'établir une référence croisée sur les paramètres (qui
peut être le 2ème ou le 3ème) si je procède par grep successif, comment je
peux concaténer une liste à une autre ?

J'aimerais bien un peu d'aide.

Si je me fourvoie sur le langage adopté, le problème est que je ne connais
pas perl ni python ni... à la rigueur je peux prendre une solution en
emacs-lisp :)

J'ai bien le "advanced bash scripting" mais il ne m'a pas aidé pour cet
usage un peu plus pointu de awk, ni pour les listes.

PS: je me tamponne de la portabilité, il s'agit ici de remplir une tâche
ponctuelle sur ma machine. Par contre si on me suggère tcsh ou un autre à
la place de bash, je suis ouvert.

Merci.
Sébastien Kirche

10 réponses

1 2
Avatar
Stephane Chazelas
2004-02-04, 14:00(+01), Sébastien Kirche:
[...]
cat $cb | tr 'r' 'n' | grep CB_ | awk '/CB_.*(/,/)/ { print $1}'
[...]

FailCbErr(CB_LockFile(gCIALRef,CAISSE,CBLCKEX));
FailCbErr(CB_Acces
if
FailCbErr(CB_GetRecordPos
FailCbErr(CB_Acces
FailCbErr(CB_GetRecordPos
FailCbErr(CB_Notify(gCIALRef,CAISSE,CB_ADD,0,ALLNOTIFY,lPos));
FailCbErr(CB_LockRecordVisu(gCIALRef,CAISSE,DataFen.bModification,lPos));
FailCbErr(CB_SetRecordPos(gCIALRef,gCaisseIndex,lPos,&fCaisse));
FailCbErr(CB_Acces(gCIALRef,EQGREATER,IDO_CAISSE,&Doc));

Je ne comprend pas les lignes "if" ni les "failcberr..." moi je voudrais
juste l'appel CB_LockFile(...) ou CB_Acces(...) --> problème de regexp avec
awk ?


Si le fichier contient

if (CB_LockFile...

C'est normal que ton truc renvoie 'if' ($1)

En gros, si les lignes (CR ended) 12 et 13 du fichier foo.c
contiennent:

if (FailCbErr(CB_LockRecordVisu(gCIALRef,
CAISSE,DataFen.bModification,lPos));

Tu voudrais:

foo.c:12:CB_LockRecordVisu:CAISSE

j'ai bon?

Peut-etre quelquechose comme

gawk -v 'RS=[()]' -v 'FS=W*,W*' '
FNR == 1 {n=1}
/<CB_/ {
match($0, /<CB_w*/)
f = substr($0, RSTART, RLENGTH)
a = substr($0, 1, RSTART - 1)
a = gsub("r", "", a)
o=$0
getline
printf "%s:%d:%s:%sn", FILENAME, n + a, f, $2
$0 = o $0
}
{ n += gsub("r", "") }' /path/to/*.c

--
Stéphane ["Stephane.Chazelas" arobase "free.fr"]

Avatar
Pascal Bourguignon
Sébastien Kirche writes:

Bonjour aux grands gourous du shell :)

Soit un ensemble de fichiers .c d'un projet conséquent (+ de 500).
Certains de ces fichiers appellent un ensemble de fonctions maison dont le
nom commence toujours par la forme "CB_" et dont les arguments comportent
au moins 2 arguments (le premier est un pointeur - on s'en tamponne, mais
le second est un define que j'essaie de récupérer dans mon xref)

Je m'explique un peu plus en détail :
j'essaie de fabriquer un fichier texte contenant la liste des modules c
faisant ces appels l'aide de grep.
Et pour chaque fichier trouvé, j'aimerais la liste des fonctions cb_qqchose
appelées avec leurs paramètres : encore un grep
Et j'aimerais établir une liste du 2ème paramètre de mes fonctions
cb_qqchose avec les liste des fichiers où il se trouve et si possible la
ligne...



Je me débrouille en shell en général, mais je suis débutant en sed/awk qui
me semblent indiqués pour la tâche.


Pour bien faire, il faudrait parser les sources (après les avoir
passés au préprocesseur C!). On peut se contenter d'un parseur limité,
mais il en faut un si on veut traiter par exemple:

CB_f1(w,CB_f2(x,f(y)),
z);


Si c'est pour une seule fois, et si on se contente d'à peu près, on
peut faire avec awk.


En plus les fichiers sources sont encodés pour mac, ce qui complique encore
un peu la tâche.


Non, ça ça ne pose aucune difficulté: tr '15' '12' < fic.mac > fic.unix


J'ai commencé un script shell, mais j'ai déjà un problème pour obtenir les
fonctions cb_qqchose : j'ai des faux positifs...

Voici mon embryon de script qui cafouille déjà:
-------------------------
#! /bin/bash

CFiles=`ls /Volumes/SeKiOS9/User/dev_prj/Sage/Columbia/source/*.c`

for c in $CFiles
do
CBAccesFiles=`grep -l CB_ $c` #| xargs -n 1 basename
for cb in $CBAccesFiles
do
basename $cb
cat $cb | tr 'r' 'n' | grep CB_ | awk '/CB_.*(/,/)/ { print $1}'
done
done
-------------------------

Qui donne (extrait):

CaisPrin.c
CB_UnLockRecordVisu(gCIALRef,
FailCbErr(CB_LockFile(gCIALRef,CAISSE,CBLCKEX));
FailCbErr(CB_Acces
if
FailCbErr(CB_GetRecordPos
FailCbErr(CB_Acces
FailCbErr(CB_GetRecordPos
FailCbErr(CB_Notify(gCIALRef,CAISSE,CB_ADD,0,ALLNOTIFY,lPos));
FailCbErr(CB_LockRecordVisu(gCIALRef,CAISSE,DataFen.bModification,lPos));
FailCbErr(CB_SetRecordPos(gCIALRef,gCaisseIndex,lPos,&fCaisse));
FailCbErr(CB_Acces(gCIALRef,EQGREATER,IDO_CAISSE,&Doc));

Je ne comprend pas les lignes "if" ni les "failcberr..." moi je voudrais
juste l'appel CB_LockFile(...) ou CB_Acces(...) --> problème de regexp avec
awk ?


Selon le locale (IIRC), grep peut être sensible à la casse ou non.

Pour le problème d'établir une référence croisée sur les paramètres (qui
peut être le 2ème ou le 3ème) si je procède par grep successif, comment je
peux concaténer une liste à une autre ?

J'aimerais bien un peu d'aide.

Si je me fourvoie sur le langage adopté, le problème est que je ne connais
pas perl ni python ni... à la rigueur je peux prendre une solution en
emacs-lisp :)


Ce serait le mieux (ou en Common-Lisp). J'ai un package Common-Lisp
que je me suis programmé il y a 8 ans pour parser du C++ et construire
le call-graph.

J'ai bien le "advanced bash scripting" mais il ne m'a pas aidé pour cet
usage un peu plus pointu de awk, ni pour les listes.

PS: je me tamponne de la portabilité, il s'agit ici de remplir une tâche
ponctuelle sur ma machine. Par contre si on me suggère tcsh ou un autre à
la place de bash, je suis ouvert.

Merci.
Sébastien Kirche


--
__Pascal_Bourguignon__ http://www.informatimago.com/
There is no worse tyranny than to force a man to pay for what he doesn't
want merely because you think it would be good for him.--Robert Heinlein
http://www.theadvocates.org/

Avatar
Stephane Chazelas
2004-02-04, 15:48(+01), Pascal Bourguignon:
[...]
Pour bien faire, il faudrait parser les sources (après les avoir
passés au préprocesseur C!
[...]


Ben non, il a dit que le deuxième argument (celui qu'il veut
récupérer) était une macro, cpp l'expandrait (idem pour les
includes).

--
Stéphane ["Stephane.Chazelas" arobase "free.fr"]

Avatar
Sébastien Kirche
On 4 Feb 2004, Stephane Chazelas wrote:


Si le fichier contient

if (CB_LockFile...

C'est normal que ton truc renvoie 'if' ($1)


Ha ?
Ben je croyais que
awk '/CB_.*(/,/)/ { print $1}'

ça filtrerait depuis la regexp debut (CB_...) jusqu'à la regexp de fin
(parenthèse fermante)...

Bon, faut que je me trouve un "awk pour les nuls" ;^)
L'ABS est vraiment un peu trop léger pour sed/awk et man ne montre pas
d'exemple.


En gros, si les lignes (CR ended) 12 et 13 du fichier foo.c
contiennent:

if (FailCbErr(CB_LockRecordVisu(gCIALRef,
CAISSE,DataFen.bModification,lPos));

Tu voudrais:

foo.c:12:CB_LockRecordVisu:CAISSE

j'ai bon?

[ un script awk que je vais tester ]


Ben pas tout à fait :)
Mais c'est proche: en fait j'aimerais séparer la sortie en 2 (ou 3
soyons fous). Voilà ce que ça donnerait (avé des vrais noms)
foo.c
12:CB_LockRecordVisu:CAISSE
50:CB_Notify:DEPOT
200:CB_Acces:COMPTEG
312:CB_Acces:CAISSE

bar.c
43:CB_Notify:CAISSE
60:CB_Acces:COMPTET
98:CB_LockRecord:CONTACT



et la réciproque:

CAISSE
foo.c : CB_LockRecordVisu(12), CB_Acces(312)
bar.c : CB_Notify(43)

COMPTET
bar.c : CB_Acces(60)

COMPTEG
foo.c: CB_Acces(200)

Bon, les n° de ligne c'est "cerise sur le gâteau", mais si j'arrive a
extraire pour un fichier les symboles et pour un symbole les fichiers qui
utilisent c'est déjà pas mal.

Merci pour ce premier jet, je vais voir si je peux pas broder dessus.
Et approfondir mon awk en prime.

Sébastien Kirche

Avatar
Stephane Chazelas
2004-02-04, 16:59(+01), Sébastien Kirche:
[...]
Tu voudrais:

foo.c:12:CB_LockRecordVisu:CAISSE

j'ai bon?
[...]


Ben pas tout à fait :)
Mais c'est proche: en fait j'aimerais séparer la sortie en 2 (ou 3
soyons fous). Voilà ce que ça donnerait (avé des vrais noms)
foo.c
12:CB_LockRecordVisu:CAISSE
50:CB_Notify:DEPOT
200:CB_Acces:COMPTEG
312:CB_Acces:CAISSE

[...]

et la réciproque:

CAISSE
foo.c : CB_LockRecordVisu(12), CB_Acces(312)
bar.c : CB_Notify(43)


Le format:

foo.c:12:CB_LockRecordVisu:CAISSE
foo.c:50:CB_Notify:DEPOT
foo.c:200:CB_Acces:COMPTEG
foo.c:312:CB_Acces:CAISSE
bar.c:43:CB_Notify:CAISSE
bar.c:60:CB_Acces:COMPTET
bar.c:98:CB_LockRecord:CONTACT

est quand même plus condensé et plus facile à post-processer. De
plus, il est facile d'obtenir tes formats à partir:

awk -F: -v OFS=: '$1 != o {o=$1; print o}
{ print "t" $2,$3,$4 }'

et

sort -t: -k4,4 -k1,1 | awk -F: '
$4 != o {o = $4; printf n o; p=""; n="nn"}
$1 != p {p = $1; printf "nt%s : ", $1; c=""}
{ printf "%s(%d)", c $3, $2; c=", " }
END{print ""}'

--
Stéphane ["Stephane.Chazelas" arobase "free.fr"]


Avatar
Sébastien Kirche
On 4 fév 2004, Stephane Chazelas wrote:


Le format:

foo.c:12:CB_LockRecordVisu:CAISSE
foo.c:50:CB_Notify:DEPOT
foo.c:200:CB_Acces:COMPTEG
foo.c:312:CB_Acces:CAISSE
bar.c:43:CB_Notify:CAISSE
bar.c:60:CB_Acces:COMPTET
bar.c:98:CB_LockRecord:CONTACT

est quand même plus condensé et plus facile à post-processer. De
plus, il est facile d'obtenir tes formats à partir:

awk -F: -v OFS=: '$1 != o {o=$1; print o}
{ print "t" $2,$3,$4 }'

et

sort -t: -k4,4 -k1,1 | awk -F: '
$4 != o {o = $4; printf n o; p=""; n="nn"}
$1 != p {p = $1; printf "nt%s : ", $1; c=""}
{ printf "%s(%d)", c $3, $2; c=", " }
END{print ""}'


Ah oui effectivement, j'avais pas vu ça comme cela.

On peut toujours garder le premier parcours comme résultat intermédiaire
pour pouvoir ensuite le triturer et remettre en forme.

Bon j'essaie ça demain.
Merci :)

Sébastien Kirche

Avatar
Stephane Chazelas
2004-02-04, 23:36(+01), Sébastien Kirche:
[...]
sort -t: -k4,4 -k1,1 | awk -F: '
$4 != o {o = $4; printf n o; p=""; n="nn"}
$1 != p {p = $1; printf "nt%s : ", $1; c=""}
{ printf "%s(%d)", c $3, $2; c=", " }
END{print ""}'
[...]


Bon j'essaie ça demain.
[...]


Oups,

sort -t: -k4,4 -k1,1 | awk -F: '
$4 != o {o = $4; printf "%s", n o; p=""; n="nn"}
$1 != p {p = $1; printf "nt%s : ", $1; c=""}
{ printf "%s(%d)", c $3, $2; c=", " }
END{print ""}'

plutôt.

--
Stéphane ["Stephane.Chazelas" arobase "free.fr"]


Avatar
Sébastien Kirche
Raaahh :^D
Tout comme je voulais...

J'ai compilé le premier script awk avec les 2 autres dans 2 scripts
distincts index.sh et xref.sh.

Extrait de la sortie :

index.sh
./planaAss.c
279:CB_Acces:EQGREATER
285:CB_Acces:LESSER
288:CB_GetFI:ENUMANAL
522:CB_GetRecordPos:ENUMANAL
524:CB_Acces:SUIV
./planaRup.c
130:CB_Acces:EQGREATER
138:CB_Acces:EQGREATER
151:CB_Acces:EQLESSER
160:CB_Acces:EQLESSER
164:CB_GetFI:lpGetElt->nFile
565:CB_GetRecordPos:ENUMANAL
566:CB_Notify:ENUMANAL

xref.sh
COMPTEGBUDGETA
./plana.c : CB_GetFI(1103)
./planc.c : CB_GetFI(704)

COMPTEGNOTE
./plancebl.c : CB_LockFile(100), CB_GetIndMod(116), CB_GetRecordPos(117), CB_UnLockFile(118), CB_Notify(120), CB_GetRecordPos(127), CB_UnLockFile(128), CB_Notify(130), CB_UnLockFile(139), CB_UnLockFile(145), CB_GetFI(253)

COMPTET
./plana.c : CB_GetFI(1165)
./planc.c : CB_GetFI(530), CB_GetRecordPos(686), CB_IsRecLockMo(687)
./plancepr.c : CB_GetRecordPos(1171), CB_LockRecord(1229), CB_UnLockRecord(1242), CB_UnLockRecord(1247), CB_GetRecordPos(1273), CB_IsRecLockMo(1274)


Impeccable :)

Marrant je vois aussi que ça marche quand le paramètre extrait est de la
forme structure->champ au lieu de MACRO. Bon ça n'a rien d'extraordinaire
mais c'est cool pour mon traitement :)

Faudrait que j'affine un poil l'extraction car je me suis rendu compte que
pour quelques fonctions CB_, j'ai besoin du 2ème paramètre et/ou du 3ème.
Vu que que awk ça ressemble furieusement à du C je dois pouvoir m'en tirer.

J'ai aussi reçu le parser ecrit en lisp de Pascal Bourguignon que je
remercie, mais j'ai oublié de me le forwarder au boulot. Je l'essaierai
plus tard.

Mais pour mes references croisées avec un petit bout de awk
vite-fait-sur-le-gaz j'ai déjà un résultat très satisfaisant.
Faut vraiment que je passe du temps avec awk...

Encore merci !

Cordialement,
Sébastien Kirche
Avatar
Stephane Chazelas
2004-02-05, 11:35(+01), Sébastien Kirche:
[...]
J'ai aussi reçu le parser ecrit en lisp de Pascal Bourguignon que je
remercie, mais j'ai oublié de me le forwarder au boulot. Je l'essaierai
plus tard.
[...]


Attention, tu ne peux utiliser un parseur C que si tu passes le
fichier au travers d'un préprocesseur (et tu ne peux passer par
le pre-processeur si tu veux récupérer le nom de macros de
preprocessing).

En revanche un parsing du type de ce celui qui est fait dans les
première phases du preprocessing peut t'aider (pour débrouiller
ce qui est string/commentaire...), mais je suis aussi d'avis
qu'une bidouille awk ou perl devrait être suffisante pour ton
problème (on peut débrouiller se qui est commentaire/string
assez facilement et suffisemment rigoureusement aussi en perl
par des regexps).

--
Stéphane ["Stephane.Chazelas" arobase "free.fr"]

Avatar
Sébastien Kirche
Bonjour,

même avec le "GAWK: Effective AWK Programming" sous les yeux, il ya des
trucs que je n'ai pas compris.
Si je peux me permettre, pourriez-vous m'éclairer un peu ?

On 4 Feb 2004, Stephane Chazelas wrote:

[...]


gawk -v 'RS=[()]' -v 'FS=W*,W*' '
FNR == 1 {n=1}
/<CB_/ {
match($0, /<CB_w*/)
f = substr($0, RSTART, RLENGTH)
a = substr($0, 1, RSTART - 1)
a = gsub("r", "", a)
o=$0
getline
printf "%s:%d:%s:%sn", FILENAME, n + a, f, $2
$0 = o $0
}
{ n += gsub("r", "") }' /path/to/*.c


Bon, le gros, avec l'incrémentation du compteur de ligne je pense avoir
saisi, mais il reste 3 points :
1) je comprends la regexp du field separator, mais pas celle record
separator [()]
2) a quoi sert la sauvegarde du $0 dans o ? J'ai vu que getline tout seul
affecte $0, NF, FNR, et NR. Donc pourquoi sauver $0 ? Est-ce lié à
FILENAME si la règle s'applique dès la première ligne du fichier visité ?
3) qu'est-ce que l'on calcule avec a ? est-ce dans le cas où l'instruction
CB_ s'étend sur plus qu'une ligne pour ajuster le compteur en
conséquence ?

Je vous remercie.

Sébastien Kirche

1 2