user915783
user915783

Reputation: 699

TCL script to replace C file line

I have C file at

C:\SVN\Code\fileio.c

This reads 2 audio files as

tuningFile = fopen("../../simulation/micdata.bin", "rb");   
mic1File = fopen("../../simulation/mic1.pcm", "rb");

I need to write TCL script code that will read the C file, and replace these 2 occurrences to

tuningFile = fopen("C:/SVN/simulation/micdata.bin", "rb");    
mic1File = fopen("C:/SVN/simulation/mic1.pcm", "rb");

Can anyone give a compact example for something like below:

Thanks
sedy

Upvotes: 1

Views: 510

Answers (4)

Peter Lewerin
Peter Lewerin

Reputation: 13252

package require fileutil

set filename C:/SVN/Code/fileio.c
set mapping [list {fopen("../../simulation/} {fopen("C:/SVN/simulation/}]

proc replace {mapping data} {
    string map $mapping $data
}

::fileutil::updateInPlace $filename [list replace $mapping]

Should work too. (Definition of mapping nicked from Donal.) updateInPlace calls the command prefix in its second argument, passes the contents of the file to that command, and updates the file with the result from the command.

This is very nearly the same procedure as in Donal's answer, expressed in higher-level code. If you want a backup copy, do this before calling updateInPlace:

file copy $filename [file rootname $filename].bak

Documentation: fileutil package, list, proc, set, string

Upvotes: 1

glenn jackman
glenn jackman

Reputation: 246807

Here's a version that extracts the filename and uses file normalize on it:

set f [open $filename r]
set code [read $f]
close $f

set code [subst -novar -noback [regsub -all {((?:tuningFile|mic1File) = fopen\(")([^"]+)} $code {\1[file normalize "\2"]}]]

Breaking that up,

  1. this command

    regsub -all {((?:tuningFile|mic1File) = fopen\(")([^"]+)} $code {\1[file normalize "\2"]}
    

    will find the string tuningFile = fopen("../relative/file (or "mic1file = ...") and replace it with the text

    tuningFile = fopen("[file normalize "../relative/file"]
    
  2. Then we feed that to subst so that embedded commands can be substituted, executing that file normalize command, resulting in the text

    tuningFile = fopen("/full/path/to/file
    

Take 2: handle brackets in C code

$ pwd
/home/jackman/tmp/base/SVN/Code

$ tree ../..
../..
├── SVN
│   └── Code
│       ├── fileio.c
│       └── normalize.tcl
└── simulation
    ├── mic1.pcm
    └── micdata.bin

3 directories, 4 files

$ cat fileio.c 
int tuningFix[MAXTUNING];
tuningFile = fopen("../../simulation/micdata.bin", "rb");   
mic1File = fopen("../../simulation/mic1.pcm", "rb");

$ cat normalize.tcl 
#! tclsh
package require fileutil
set code [fileutil::cat [lindex $argv 0]]

# protect existing brackets
set bracketmap [list \[ \x01 \] \x02]
set code [string map $bracketmap $code]

# normalize filenames
set code [
    subst -novar -noback [
        regsub -all {((?:tuningFile|mic1File) = fopen\(")([^"]+)} $code {\1[file normalize "\2"]} 
    ]
]

# restore brackets
set code [string map [lreverse $bracketmap] $code]

puts $code

$ tclsh normalize.tcl fileio.c 
int tuningFix[MAXTUNING];
tuningFile = fopen("/home/jackman/tmp/base/simulation/micdata.bin", "rb");   
mic1File = fopen("/home/jackman/tmp/base/simulation/mic1.pcm", "rb");

Upvotes: 1

user915783
user915783

Reputation: 699

based on great help from all users who commented, I was able to do the task as

proc replaceFileTemp {} {
    global pth_fileio_orig

    # create backup for copy back
    set pth_backup [file rootname $pth_fileio_orig].bak
    file copy $pth_fileio_orig $pth_backup

    #get current file path
    set thisFilePth [ dict get [ info frame [ info frame ] ] file ]

    # get folder for current file
    set thisFileFolderPth [file dirname $thisFilePth]

    # set the replacement string/path
    set replacementPth [file dirname $thisFileFolderPth]


    # obtain original string to be replaced 
    set origPth "../../simulation/toplevel"

    # download package for file manipulation
    package require fileutil


    set mapping [list $origPth $replacementPth]
    proc replace {mapping data} {
    string map $mapping $data
    }

    # replace original string with replacement string for all occurrences in file
    ::fileutil::updateInPlace $pth_fileio_orig [list replace $mapping]
}

# set the path to toplevel C file
set pth_fileio_orig [file normalize "../../../fileio.c"]
replaceFileTemp

Upvotes: 0

Donal Fellows
Donal Fellows

Reputation: 137567

The key is that you actually want to replace:

fopen("../../simulation/

with

fopen("C:/SVN/simulation/

That's easily done with string map. The rest of your problem is then just a matter of doing the file I/O, and pretty much any C source file that can be compiled by an ordinary compiler is best processed by loading it all into memory at once:

set filename {C:\SVN\Code\fileio.c}
set mapping [list {fopen("../../simulation/} {fopen("C:/SVN/simulation/}]

# Slurp the file in
set f [open $filename]
set data [read $f]
close $f

# Apply the mapping
set data [string map $mapping $data]

# Make the original a backup
file rename $filename $filename.bak

# Write back with a separate open
set f [open $filename w]
puts -nonewline $f $data
close $f

If you prefer, you can get the filename as an argument using, say, [lindex $argv 0]. The rest of the code doesn't care.

Upvotes: 2

Related Questions