Article 8578 of alt.sources:
Xref: mv alt.sources:8578 comp.lang.c:153075
Path: mv!news.sprintlink.net!news-fw-22.sprintlink.net!hpg30a.csc.cuhk.hk!news.cuhk.edu.hk!news.uoregon.edu!vixen.cso.uiuc.edu!uwm.edu!news.cse.psu.edu!news.math.psu.edu!ra.nrl.navy.mil!mitch.nrl.navy.mil!grunes
From: grunes@news.nrl.navy.mil (Mitchell R Grunes)
Newsgroups: alt.sources,comp.lang.c
Subject: diagramc: Simple Diagrammer for C Language Programs
Date: 29 Aug 1996 13:23:27 GMT
Organization: AlliedSignal Technical Services Corp.
Lines: 649
Message-ID: <grunes.426.0@news.nrl.navy.mil>
NNTP-Posting-Host: mitch.nrl.navy.mil

Archive-name: diagramc
Submitted-by: grunes@ssdd.nrl.navy.mil (Mitchell R Grunes)

********diagramc: Simple Diagrammer for C Language Programs********

The most recent changes: column aligment fixed, improved Unix and Vax
VMS scripts, seperated pre-processor and normal code diagramming.

If you like or dislike it, send e-mail.  DON'T POST to alt.sources.
Bug reports must include sample code on which it failed.

CONTENTS:
   diagramc.for  Fortran language source code.
   diagramc.bat  MS-DOS procedure to run without answering questions.
   diagramc.sh   Unix csh procedure to run without answering questions.
   diagramc.vax  VAX VMS DCL procedure to run without answering questions.
---diagramc.for--------------CUT HERE-----------------------------
c EXAMPLE OF OUTPUT (looks better if you choose IBM PC line graphics):

c    +------ I_Hate_C() {                                     |   1
c    |+------- if (You_Like(C)) {                             |   2
c    ||          BoyOrGirl=Bad;                               |   3
c  +-||      #ifdef SMART                                     |   4
c  | ||          ReEducate();                                 |   5
c  +-||      #endif                                           |   6
c    |+------- } else {                                       |   7
c    ||          BoyOrGirl=Good;                              |   8
c    |+------- }                                              |   9
c    +------ }                                                |  10

c Diagrams C language {} constructs, case and default,
c  and puts a * next to goto, break, continue, exit and return.  It can
c  place = next to comment blocks.
c Up to 2 levels of preprocessor constructs (#if--#elif--#endif) are
c  diagrammed seperately, on the outside.

c Program by Mitchell R Grunes, ATSC/NRL (grunes@ssdd.nrl.navy.mil).
c Revision date: 8/25/96.
c If you find it useful, or find a problem, please send me e-mail.

c This program was written in FORTRAN, for historic reasons.
c (For this reason, people who mostly program in C will probably be
c  unwilling to use this program, even as a utility.)

c WARNING: The "/*" sequences will confuse compilers like SGI Fortran
c  that use a C pre-processor by default on Fortran programs, so you
c  must use a compiler switch like "-nocpp" to turn that off.

c It can be confused if an INCLUDE block contains a structure that
c  begins inside and ends outside (or vice-versa).

c  It also does not diagram IF, FOR, ELSE, WHILE, etc., unless you use
c   { and } to enclose the conditionally executed statement--
c   e.g. it will not draw any lines next to

c       if(condition)
c         for (i=0; i<10; i++)
c           a[i]=2;
c       else
c         b=3;

c I hope this works for you, but bear in mind that nothing short of
c  a full-fledged language parser could really do the job.  Perhaps
c  worth about what you paid for it.    (-:

c Versions: To diagram Fortran:     diagramf.for
c                      IDL/PV-WAVE: diagrami.for
c                      C:           diagramc.for
c MS-DOS procedures to call above programs without asking so many questions,
c  append output to file diagram.out:
c                      Fortran:     diagramf.bat (card format)
c                                   diagram9.bat (free format)
c                      IDL/PV-WAVE: diagrami.bat
c                      C:           diagramc.bat
c Similar Unix csh procedures:
c                      Fortran:     diagramf.sh  (card format)
c                                   diagram9.sh  (free format)
c                      IDL/PV-WAVE: diagrami.sh
c                      C:           diagramc.sh
c Similar Vax VMS DCL procedures:
c                      Fortran:     diagramf.vax (card format)
c                                   diagram9.vax (free format)
c                      IDL/PV-WAVE: diagrami.vax
c                      C:           diagramc.vax

        program diagramc                                ! Diagrammer for C
        character*80 filnam,filnam2

        print*,'C source filename?'
        read(*,'(a80)')filnam
        print*,filnam

        print*,'Output file (blank=screen)?'
        read(*,'(a80)')filnam2
        print*,filnam2

        print*,'Column in which to write line #''s ',
     &   '(67 for 80 col screen, 0 for none):'
        read*,LCol
        print*,LCol

        print*,'Notate comments with = (0=no, 1=yes):'
        read*,inotate
        print*,inotate

        print*,'Use IBM PC graphics characters (0=no):'
        read*,iGraphics
        print*,iGraphics

        call diagram(filnam,filnam2,LCol,inotate,iGraphics)
        end
c-----------------------------------------------------------------------
        subroutine diagram(filnam,filnam2,LCol,inotate,
     &   iGraphics)
c Program by Mitchell R Grunes, ATSC/NRL (grunes@ssdd.nrl.navy.mil).
        character*80 filnam,filnam2
        character*160 a,b,bsave
        character*5 form
        character*8 fm
        character*1 c
        logical fout
        logical find
        external find
        common icol

c Symbols which will mark block actions:
        character*1 BlockBegin    (2) /'+','+'/  ! Start of block
        character*1 BlockEnd      (2) /'+','+'/  ! End of block
        character*1 BlockElse     (2) /'+','+'/  ! Else construct
        character*1 BlockContinue (2) /'|','|'/  ! Block continues w/o change
        character*1 BlockHoriz    (2) /'-','-'/  ! Horizontal to start of line
c Same, but allows horizontal line to continue through:
        character*1 BlockBeginH   (2) /'+','+'/  ! Start of block
        character*1 BlockEndH     (2) /'+','+'/  ! End of block
        character*1 BlockElseH    (2) /'+','+'/  ! Else construct

        if(iGraphics.ne.0)then
          iGraphics=1

          BlockBegin   (1)=char(218)            ! (1)=normal
          BlockEnd     (1)=char(192)
          BlockElse    (1)=char(195)
          BlockContinue(1)=char(179)
          BlockHoriz   (1)=char(196)
          BlockBeginH  (1)=char(194)
          BlockEndH    (1)=char(193)
          BlockElseH   (1)=char(197)

          BlockBegin   (2)=char(214)            ! (2)=DO/FOR loops (doubled)
          BlockEnd     (2)=char(211)            ! (not yet used)
          BlockEnd     (2)=char(211)
          BlockElse    (2)=char(199)
          BlockContinue(2)=char(186)
          BlockHoriz   (2)=char(196)
          BlockBeginH  (2)=char(209)
          BlockEndH    (2)=char(208)
          BlockElseH   (2)=char(215)
        endif

        open(1,file=filnam,status='old')
        fout=filnam2.gt.' '
        if(fout)open(2,file=filnam2,status='unknown')
                                                ! ASCII 12 is a form feed
        if(fout)write(2,*)char(12),
     &   '=============--',filnam(1:LenA(filnam)),'--============='

        if(fout)     write(2,'(11x,a50,a49,/)')  ! Write column header
     &   '....,....1....,....2....,....3....,....4....,....5',
     &   '....,....6....,....7....,....8....,....9....,....'
        if(.not.fout)write(*,'(11x,a50,a49,/)')' ',
     &   '....,....1....,....2....,....3....,....4....,....5',
     &   '....,....6....,....7....,....8....,....9....,....'

        i3=0                                    ! # nest levels after
                                                !  current line
        i3pp=0                                  ! same for pre-processor
        nline=0
        icomment=0                              ! not inside comment
        iunit=1
10      a=' '
        read(iunit,'(a160)',end=99)a
        nline=nline+1
        fm=' '
        write(fm,'(i5)')nline
        form=fm

        if(a(1:1).eq.char(12))then
          if(fout)write(2,'(a1,:)')char(12)
          if(.not.fout)print*,'------------FORM FEED------------'
          b=a(2:160)
          a=b
        endif

        b=' '                                   ! Turn tabs to spaces
        j=1
        do i=1,LenA(a)
          if(a(i:i).eq.char(9))then
            j=(j-1)/8*8+8+1
          elseif(j.le.160)then
            b(j:j)=a(i:i)
            j=j+1
          endif
        enddo

        a=b
        bsave=b
        b=' '
        i1=i3                                   ! # nest levels before
                                                !  current line
        i1pp=i3pp                               ! same for pre-processor
        i4=0                                    ! not 0 to flag start or end
                                                !  of block
        i4pp=0
        iquote=0                                ! no ' yet
        idquote=0                               ! no " yet
        icomment2=0                             ! anything outside comment?
        icomment3=icomment                      ! no comment occurred?
        i=1
        j=1
        dowhile(i.le.160)                       ! handle upper case
          c=a(i:i)
          if(c.ge.'A'.and.c.le.'Z')c=char(ichar(c)+32)
          if(c.eq.''''.and.idquote.eq.0.and.icomment.eq.0)then
            iquote=1-iquote
            if(i.gt.1)then
                                                ! char(92) is \
              if(iquote.eq.0.and.a(i-1:i-1).eq.char(92))
     &         iquote=1-iquote
            endif
          endif
          if(c.eq.'"' .and.iquote .eq.0.and.icomment.eq.0)then
            idquote=1-idquote
            if(i.gt.1)then
              if(idquote.eq.0.and.a(i-1:i-1).eq.char(92))
     &         idquote=1-idquote
            endif
          endif
          if(c.eq.'/'.and.i.lt.160.and.iquote.eq.0.and.idquote.eq.0)    ! /* ?
     &     then
            if(a(i+1:i+1).eq.'/')icomment3=1    ! // is C++ comment line
            if(a(i+1:i+1).eq.'/')go to 15
            if(a(i+1:i+1).eq.'*')then
              if(icomment.ne.0)then
                PRINT*,'***WARNING--nested comment line',form
                if(fout)print*,b
                print*,char(7)
              endif
              icomment=1
              icomment3=1
              c=' '
              i=i+1
            endif
          endif
          if(c.eq.'*'.and.i.lt.160.and.iquote.eq.0.and.idquote.eq.0)    ! */?
     &     then
            if(a(i+1:i+1).eq.'/')then
              if(icomment.eq.0)then
                PRINT*,'***WARNING--*/ without /* clause line',form
                if(fout)print*,b
                print*,char(7)
              endif
              icomment=0
              c=' '
              i=i+1
            endif
          endif
          if(icomment.ne.0)c=' '
          if(c.ne.' ')icomment2=1
          if(c.eq.'{')then
            if(fout.and.i3.eq.0)print*,'Line ',form,' ',a(1:LenA(a))
            i3=i3+1
          elseif(c.eq.'}')then
            i3=i3-1
            i4=max(i4,i1-i3)
            if(i3.lt.0)then
              PRINT*,'***ERROR--INVALID DIAGRAMMING INDEX line',
     &         form
              if(fout)
     &         WRITE(2,*)'***ERROR--INVALID DIAGRAMMING INDEX!***'
              if(fout)print*,b
              print*,char(7)
              i3=max(i3,0)
            endif
          endif
          if(j.le.160) b(j:j)=c
          if(j.gt.1)then                        ! (kill multiple spaces)
            if(c.eq.' '.and.b(j-1:j-1).eq.' ')j=j-1
          endif
          j=j+1
          i=i+1
        enddo

15      if(find(b,'#if',2).or.find(b,'# if',2))then
          i3pp=i3pp+1
          i4pp=1
        elseif(find(b,'#else',2).or.find(b,'# else',2)
     &  .or.find(b,'#elif',2).or.find(b,'# elif',2))then
          i4pp=1
        elseif(find(b,'#endif',2).or.find(b,'# endif',2))then
          i3pp=i3pp-1
          i4pp=1
        endif

        igoto=0                                 ! no goto on line
        if(find(a,'go to',64+512).or.find(a,'goto',64+512)
     &   .or.find(a,'return',32+512)
     &   .or.find(a,'break',32+512).or.find(a,'continue',32+512)
     &   .or.find(a,'exit',32+512))igoto=1

        if(find(b,'case',32+512).or.
     &   find(b,'default ',512).or.find(b,'default:',512))i4=max(1,i4)

20      b=bsave
        a=' '
        if(i1  .lt.0.or.i3  .lt.0.or.i4  .lt.0.or.
     &     i1pp.lt.0.or.i3pp.lt.0.or.i4pp.lt.0)then
          PRINT*,'***ERROR--INVALID DIAGRAMMING INDEX line',form
          if(fout)WRITE(2,*)'***ERROR--INVALID DIAGRAMMING INDEX!***'
          if(fout)print*,b
          print*,char(7)
          i1=max(i1,0)
          i3=max(i3,0)
          i4=max(i4,0)
          i1pp=max(i1pp,0)
          i3pp=max(i3pp,0)
          i4pp=max(i4pp,0)
        endif

        i2=max(i1,i3)                           ! # of nests on current line
        i4=max(i4,iabs(i3-i1))                  ! not 0, to flag start or
                                                !  end of block
        i2pp=max(i1pp,i3pp)
        i4pp=max(i4pp,iabs(i3pp-i1pp))

        iBlock=1                                ! For the present version.

        a=' '                                   ! Leave space for diagram
        a(12:160)=b                             !  (must match column header)

        LastUse=1                               ! Last usable diagram col
        dowhile(LastUse.lt.160.and.a(LastUse:LastUse).eq.' ')
          LastUse=LastUse+1
        enddo
        LastUse=LastUse-2

        if(igoto.ne.0)a(1:1)='*'                ! Place * next to jumps
        if(icomment2.eq.0.and.icomment3.ne.0..and.inotate.ne.0)
     &   a(1:1)='='

        if(i2pp.gt.0)then                       ! Draw one vertical line per
          do i=2,min(i2pp+1,3)                  !  nest level.
            a(i:i)=BlockContinue(iBlock)
          enddo
        endif

        if(i4pp.ne.0)then                       ! Draw horizontal lines inward
          do i=i2pp+2,3                         !  from above.
            a(i:i)=BlockHoriz(iBlock)
          enddo
        endif

        do i=0,i4pp-1                           ! May need to replace some
                                                !  vertical lines with
                                                !       else  symbol
          c=                  BlockElse(iBlock) !  or   begin symbol
          if(i1pp+i.lt.i3pp)c=BlockBegin(iBlock)!  or   end   symbol
          if(i1pp+i.gt.i3pp)c=BlockEnd  (iBlock)
          j=max(2,min(3,i2pp+1-i))
          a(j:j)=c
          if(a(j+1:j+1).eq.BlockElse  (iBlock)) ! Continue horizontal lines
     &       a(j+1:j+1)  = BlockElseH (iBlock)
          if(a(j+1:j+1).eq.BlockBegin (iBlock))
     &       a(j+1:j+1)  = BlockBeginH(iBlock)
          if(a(j+1:j+1).eq.BlockEnd   (iBlock))
     &       a(j+1:j+1)  = BlockEndH  (iBlock)
        enddo

        if(i2.gt.0)then                         ! Same for non-pre-processor
          do i=4,min(i2+3,LastUse)
            a(i:i)=BlockContinue(iBlock)
          enddo
        endif

        if(i4.ne.0)then
          do i=i2+4,LastUse
            a(i:i)=BlockHoriz(iBlock)
          enddo
        endif

        do i=0,i4-1

          c=              BlockElse(iBlock)
          if(i1+i.lt.i3)c=BlockBegin(iBlock)
          if(i1+i.gt.i3)c=BlockEnd  (iBlock)
          j=max(4,min(LastUse,i2+2+1-i))
          a(j:j)=c
          if(a(j+1:j+1).eq.BlockElse  (iBlock))
     &       a(j+1:j+1)  = BlockElseH (iBlock)
          if(a(j+1:j+1).eq.BlockBegin (iBlock))
     &       a(j+1:j+1)  = BlockBeginH(iBlock)
          if(a(j+1:j+1).eq.BlockEnd   (iBlock))
     &       a(j+1:j+1)  = BlockEndH  (iBlock)
        enddo

        if(LCol.gt.0.and.a(max(1,LCol+11):160).eq.' ')then       ! line #
          if(form(1:1).eq.' ')form(1:1)=BlockContinue(iBlock)
          a(LCol+11:160)=form
        endif

        n=LenA(a)                               ! Output diagrammed line
        if(fout)     write(2,'(80a1,80a1)')(a(i:i),i=1,n)
        if(.not.fout)write(*,'(1x,80a1,80a1)')(a(i:i),i=1,n)

        i1=i3
        i1pp=i3pp
        goto 10
99      if(iunit.eq.3)then
          iunit=1
          i1=i1-1
          i1pp=i1pp-1
          close(3)
          goto 10
        endif
        if(i3.gt.0.or.i3pp.gt.0)then
          PRINT*,'***WARNING--SOME NEST LEVELS LEFT HANGING AT END***'
          print*,char(7)
        endif
        end
c-----------------------------------------------------------------------
        logical function find(a,b,icond)        ! find b in a, subject to
                                                !  conditions:
                                                ! icond=sum of the following:
                                                ! 2:  Must be first non-blank
                                                ! 32: Next character not alphanumeric
                                                ! 64: Next character not alphabetic
                                                ! 512  Prior character, if present,
                                                !      must be blank or ) or }
                                                !      or { or ;
c Program by Mitchell R Grunes, ATSC/NRL (grunes@ssdd.nrl.navy.mil).
c Revision date: 8/25/96.
        character*(*) a,b
        character*1   c,cNext
        common icol
        logical result

        ii=len(a)
        jj=len(b)
        result=.false.
        do i=1,ii-jj+1
          if(a(i:i+jj-1).eq.b)then
            icol1=i                             ! icol1=column of item found
            icol =i+jj                          ! icol =colomn after item
                                                !  found
            c=' '
            cNext=' '
            if(icol1.gt.1)c=a(icol1-1:icol1-1)
            if(icol .le.ii)cNext=a(icol:icol)

            result=.true.

            if(result.and.iand(icond,2).ne.0.and.icol1.gt.1)then
              result=a(1:icol1-1).eq.' '
            endif

            if(result.and.iand(icond,32).ne.0)
     &       result=(cNext.lt.'0'.or.cNext.gt.'9').and.
     &              (cNext.lt.'a'.or.cNext.gt.'z')

            if(result.and.iand(icond,64).ne.0)
     &       result=(cNext.lt.'a'.or.cNext.gt.'z')

            if(result.and.iand(icond,512).ne.0)result=c.eq.' '
     &       .or.c.eq.';'.or.c.eq.')'.or.c.eq.'{'.or.c.eq.'}'

            find=result
            if(result)return
          endif
        enddo
        find=result
        return
        end
c-----------------------------------------------------------------------
        function LenA(a)                        ! Length of string, at least 1
c Program by Mitchell R Grunes, ATSC/NRL (grunes@ssdd.nrl.navy.mil).
c Revision date: 8/25/96.
        character*(*) a
        n=len(a)
        dowhile(n.gt.1.and.a(n:n).eq.' ')
          n=n-1
        enddo
        LenA=n
        end
---diagramc.bat--------------CUT HERE-----------------------------
rem                     ---diagramc.bat---
rem MS-DOS procedure to diagram a C language program.

rem  by Mitchell R Grunes.

rem I assume that the executable is in directory c:\grunes on
rem  your PC.

rem Syntax:
rem     diagramc
rem to be prompted for input parameters.

rem Alternate Syntax:
rem     diagramc filename(s)
rem to append diagram of file(s) into diagram.out

if %1a == a  c:\grunes\diagramc
if %1a == a  goto quit

echo off
:loop
  echo ========================-- %1 --========================
  rem Prompt answers: input from %1, output to diagram2.sc (for now),
  rem  place numbers in column 67, notate comments with =,
  rem  diagram pre-processor blocks, use IBM PC graphics.

  echo %1          >  diagram.sc
  echo diagram2.sc >> diagram.sc
  echo 67          >> diagram.sc
  echo 1           >> diagram.sc
  echo 1           >> diagram.sc
  echo 1           >> diagram.sc
  c:\grunes\diagramc < diagram.sc
  type diagram2.sc >> diagram.out
  del diagram.sc
  del diagram2.sc
  shift
  if not %1a == a goto loop
:quit
  echo Note--This does not delete diagram.out before appending to it.
---diagramc.sh---------------CUT HERE-----------------------------
#!/bin/csh
#              ---diagramc.sh---
#Unix csh procedure to diagram a C language program

# by Mitchell R Grunes.

#I assume that the executable and this procedure are in the search path,
#  and that this procedure has execute permission.

#Syntax:
#    diagramc.sh
#to be prompted for input parameters.

#Alternate Syntax:
#    diagramc.sh filename(s)
#to append diagram of file(s) into diagram.out

  if  (${?noclobber}) then
    unset noclobber
    set   noclobbersave
  endif

  if $1a == a  then
    diagramc
    goto quit
  endif

loop:
  echo ========================-- $1 --========================
  #Prompt answers: input from $1, output to diagram2.sc (for now),
  # place numbers in column 67, notate comments with =,
  # diagram pre-processor blocks, don't use IBM PC graphics.

  echo $1          >  diagram.sc
  echo diagram2.sc >> diagram.sc
  echo 67          >> diagram.sc
  echo 1           >> diagram.sc
  echo 1           >> diagram.sc
  echo 0           >> diagram.sc
  diagramc < diagram.sc
  cat diagram2.sc >> diagram.out
  rm diagram.sc
  rm diagram2.sc
  shift
  if ! ($1a == a) then
    goto loop
  endif
quit:
  echo Note--This does not delete diagram.out before appending to it.
  if  (${?noclobbersave}) then
    set   noclobber
    unset noclobbersave
  endif
---diagramc.vax--------------CUT HERE-----------------------------
$!                        ---diagramc.vax---
$!VAX VMS procedure to diagram a C language program
$!
$! by Mitchell R Grunes.
$!
$!I assume that the executable and this procedure are in the search path,
$!  and that this procedure has execute permission.
$!
$!Syntax:
$!    @diagramc.vax
$!to be prompted for input parameters.
$!
$!Alternate Syntax:
$!    @diagramc.vax filename(s)
$!to append diagram of file(s) into diagram.out
$
$  if P1 .EQS. ""
$  then
$    define/user sys$input sys$command
$    run diagramc
$    goto quit
$  endif
$
$  write sys$output "========================-- "+P1+" --========================"
$
$! Must pre-create diagram.out if does not exist
$  open/append/error=noSkip diagram.out diagram.out
$  goto Skip
$noSkip:
$  open/write diagram.out diagram.out
$Skip:
$  close diagram.out
$
$! Must pre-create diagram2.sc with same file attributes
$  open/write diagram2.sc diagram2.sc
$  close diagram2.sc
$
$  !Prompt answers: input from P1, output to diagram2.sc (for now),
$  ! place numbers in column 67, notate comments with =,
$  ! don't use IBM PC graphics.
$
$  open/write diagram.sc diagram.sc
$  write diagram.sc "$Run diagramc"
$  write diagram.sc P1
$  write diagram.sc "diagram2.sc"
$  write diagram.sc "67"
$  write diagram.sc "1"
$  write diagram.sc "0"
$  close diagram.sc
$  @diagram.sc
$  append diagram2.sc diagram.out
$  delete diagram.sc;*
$  delete diagram2.sc;*
$
$  if (P2 .NES. "") then @diagramc.vax 'P2' 'P3' 'P4' 'P5' 'P6' 'P7' 'P8'
$  write sys$output "Note--This does not delete diagram.out before appending to it."
$quit:
-----------------------------CUT HERE-----------------------------
-------------------------------------------------------------------------
Mitchell R Grunes, grunes@nrlvax.nrl.navy.mil.  Opinions are mine alone.



