Reputation: 699
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
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
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,
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"]
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
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
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