This is the new home of the egghelp.org community forum.
All data has been migrated (including user logins/passwords) to a new phpBB version.


For more information, see this announcement post. Click the X in the top right-corner of this box to dismiss this message.

[SOLVED] Calling a varable name from a string of another...

Help for those learning Tcl or writing their own scripts.
Post Reply
d
dj-zath
Op
Posts: 134
Joined: Sat Nov 15, 2008 6:49 am
Contact:

[SOLVED] Calling a varable name from a string of another...

Post by dj-zath »

ooOOhh!

ths ones kinda scary to explain.. so I'll explain WHY I have to do this.. perhaps my approach is wrong, and someone can recommend a better way to do it...

since my eggie actually generates specific and dynamic elements of my website, I came up with an idea to use TEMPLATE files instead of just writing mean code into the tcl scripts.. which would mean having to REWRITE the script over and over each time a change to the website was needed...

I had inital code that took a webpage.tpl file and ran it through some filters to replace given varables with the actual assigned values when needed.. For efficiency, the script worked in modes..

first mode: SetVars

here we load the template fille into a varable ONLY ONCE at startup...

Code: Select all


proc  setvars {} {
        global MyTPL TmpIDX
        if {([file exists "$MyTPL/index.tpl"])} {
               set TmpIDX "[read [open "$MyTPL/index.tpl" r]]";
        }; 
}; 

now, we only write out the new webpage when a change is detected in the varable strings.. we also parse and filter them as needed:

Second Mode: Output

Code: Select all

proc     Output {} {
           global TmpIDX MyWeb DetIA DetSA;
           if {$Csh01 != "$DetIA $DetSA"} {
                set Csh01 "$DetIA $DetSA";
                regsub -all -- "\\\$DetIA" $TmpIDX "$DetIA" MetaIDX;
                regsub -all -- "\\\$DetSA" $MetaIDX "$DetSA" MetaIDX;

                set Out [open "$MyWeb/index.html" w]; puts $Out "$MetaIDX";
                flush $Out;
                close $Out;
           };
}; 

now, this works rather nicely, however, theres a small little problem.. I need to STILL have to write in mean code for EACH new webpage that the bot needs to generate!

that, in itself makes it rather MEANINGLESS to do in the first place!

So, I started out with a new approach.. to read a directory and generate a list of .tpl files in said dir...

Code: Select all


foreach VarA [read [open $MyTPL r]] {
        if {(([string first ".tpl" $VarA]) >= "1")} {
                set VarB [read [open "$MyTPL/$VarA" r]];
                set VarA [string map {".tpl" ""} $VarA];
                global [read $VarA];
                set [read $VarA] $VarB;
        };
};

The idea here was to take a list of files, and assign them into varables...

Then, in Output..

Code: Select all


foreach VarA [read [open $MyTPL r]] {
        if {(([string first ".tpl" $VarA]) >= "1")} {
                set VarA [string map {".tpl" ""} $VarA];
                global $VarA;
                set $VarA [read [open $VarA r]];
                regsub -all -- "\\\$DetIA" $VarA "$DetIA" MetaIDX;
                regsub -all -- "\\\$DetSA" $MetaIDX "$DetSA" MetaIDX;
                set Out [open "$MyWeb/$VarA.html" w];
                puts $Out "$MetaIDX";
                flush $Out;
                close $Out;
       };
};



THIS DOESN'T WORK!

appariently, i can not assign a string from one varable as the name of another..and THEN make it global!

this is where I'm stuck!

-DjZ
:) :)
Last edited by dj-zath on Tue Nov 17, 2009 12:21 am, edited 1 time in total.
n
nml375
Revered One
Posts: 2860
Joined: Fri Aug 04, 2006 2:09 pm

Post by nml375 »

One classic approach for templates would be to use the subst command with appropriate switches. You'll find numerous examples through the forum.

No need for long lists of variables to substitute, simply open and read the template, do the subst and any variable in the data will be substituted, write the file...
NML_375
d
dj-zath
Op
Posts: 134
Joined: Sat Nov 15, 2008 6:49 am
Contact:

Post by dj-zath »

hi NML!

thanks for the reply..

Actually, thats not where I'm having the problem..

the problem is READING the template files and loading them into the bot as varables.. this is a unknown value of how many, what they are called and/or whats in them..

THEN we need to parse, subst and write them back out to the website...

this is done on EVERY change thats detected from a list of internal varables.. an example would be all the "status lights" which show up as gif images and the DJs image based on which DJ is on.. etc etc

if I read/write the directory(ies) EACH time I'm detecting a change, this means a read and write to the drive CONSTANTLY and this REALLY slows things down!

in the past, I have simply written specific parser, extract, and subst code for EACH webpage indevidually.. this accounted for a LOT of unessessary code.. and, if I needed to change something on the website, you guessed it I have to EDIT the bot too!

SO....

I thought to only READ the template files at startup and load them into RAM as varable(s).. since those don't change... then only update the webpages when needed.. to do this, I have to "float" an unknown number of GLOBAL varables whos names are not even known untill the bot can detect and read their respected tpl files first. since this happens AFTER the bot is loaded and running...

its NOT as easy as it sounds!

I can't seem to take an array's output and assign it as global varables "on the fly" AND I also can't seem to read them from an UNKNOWN list of (non existant) globals later down the line by another proc..

I see a lot of 'can't find channel named "index ' errors (not "index" , "index )

as for the subst, I have been using regsub.. and THAT part of the code works VERY nicely.. the varables that need to be subistuted ARE known and are fixed.. thats not the problem!

example:

dir /www/TPL

folder contains 6 files in which 4 are .tpl's

tpl files are webpages which contain $varables where respected.

the problem is it can be any number of VARIOUS NAMED files

index.tpl and playlist.tpl and blowme.tpl etc etc etc

down the line I may need to add MORE (or take some out)

thing is if I make them STATIC I'll be editing the bot over and over and over and over.. to make changes in the website!

I was able to create the array, and get the list of files containing only .tpl extensions..

I was able to load their contents into said varable(s)

I was then able to "chop off" the .tpl extension

but now I can *NOT* take the list of filenames and turn them into GLOBAL VARABLES

then in another proc...

I was *NOT* able to read from this "magic list of unknown global varables" and turn them BACK into said filenames containing the internal data whthin each varable

(next part assumes I create a fake list of website page varables)

I WAS able to take said varables and subst/regexp them to replace the known intenral varables with the proper values and then turn them back into webpages. i.e replace $DetIA with "onair.gif" etc... this is also done for efficiency- so that the viewer only downloads ONE graphic for all respected varables and not the same graphic, over and over and over, for EACH varable in the list.

then I was able to load the webnpages back into /www/web

well, the array won't "migrate" onto the global list it seems... I'm obviously an idiot... cause Im not getting anywhere...

-DjZ-
:) :)
d
dj-zath
Op
Posts: 134
Joined: Sat Nov 15, 2008 6:49 am
Contact:

Post by dj-zath »

UPDATE:

I'm going to try doing it all in the "output" proc.. as you suggested- to load each page, run it through a 'foreach loop', subst it and then write it back to the website..

thats going to be a LOT of disk activity!

but, I think I can do that easily enough.. then I don't need to create any global lists or varable sets..

I'll post the results when I'm done...

-DjZ-
:) :)


10 munutes later...

DONE!

heres the code:

Code: Select all

proc    Output {} {
          global Csh07 DetIA DetSA DetIV DetSV MyTPL MyWeb; 
          if {$Csh07 != "$DetIA $DetSA $DetIV $DetSV} { 
                    set Csh07 "$DetIA $DetSA $DetIV $DetSV; 
                    foreach VarA [read [open $MyTPL r]] {
                            if {(([string first ".tpl" $VarA]) >= "1")} {
                                        set VarB [read [open "$MyTPL/$VarA" r]];
                                        set VarA [string map {".tpl" ".html"} $VarA];

                                        regsub -all -- "\\\$DetIA" $VarB "$DetIA" MetaIDX;
                                        regsub -all -- "\\\$DetSA" $MetaIDX "$DetSA" MetaIDX;
                                        regsub -all -- "\\\$DetIV" $MetaIDX "$DetIV" MetaIDX;
                                        regsub -all -- "\\\$DetSV" $MetaIDX "$DetSV" MetaIDX;

                                        set Out [open "$MyWeb/$VarA" w];
                                        puts $Out "$MetaIDX";
                                        flush $Out;
                                        close $Out;
                            }; 
                    }; 
          }; 
          return 0;
}; 
and there ya have it!

works.. though I bet the drive light at the server is blinking like a MoFo hehehe

it still needs some cleaning up, but thats nothing... I'll have that done long before you get to read this!

-DjZ-
:) :)
n
nml375
Revered One
Posts: 2860
Joined: Fri Aug 04, 2006 2:09 pm

Post by nml375 »

Sorry, but reading all that text just makes me rather confused..
But if I got it all right, what you wanted was to dynamically build dependancies - IE which files depend on which variables?

For some simple variable names, you could probably get away with a simple regular expression, though in order to cover any valid variable name, you'd need a custom parser.

For performance, I'd suggest you check file modification timestamp to decide whether the dependancy-list needs updating. Or, if you keep the template in memory, 'trace' the variable containing the template.

Also, if possible, avoid using all those regsub's... they're resource-hoggers. Using 'subst' with -nocommands should replace all variables within the string in one sweep, without resorting to comples regular expressions...
NML_375
n
nml375
Revered One
Posts: 2860
Joined: Fri Aug 04, 2006 2:09 pm

Post by nml375 »

Oh, one more thing..
Your code will cause your eggdrop to run out of file handle resources, as it opens file after file without closing them. Probably best describe as a file-handle leak (kind of like a memory leak).
Depending on your system, you'd get perhaps 500 calls to "Output" before things starts to break badly...

Don't use code like this:

Code: Select all

set Var [read [open "somefile" r]]

Also, would'nt it be easier to just use the 'glob' command to get a list of .tpl-files, rather having a separate file containing a tcl-list of files?
NML_375
d
dj-zath
Op
Posts: 134
Joined: Sat Nov 15, 2008 6:49 am
Contact:

Post by dj-zath »

hi nml.. thanks for the reply!

let's see...

I DO "close" $Out.. but from what I'm getting from your reply, I'm doing it incorrectly!

(I DO note that I have seen this error happen on the Verio servers before, too)

so setting $Var with a "read open..." and then closing Var doesn't close the file handle? NOTED!

hmmmmm

-sigh-

seems like I need to scrap all this and start over... AGAIN...

not like it matters at THIS point.. my website/radio BOMBED.. I've spent TOO much time in "development" (and other issues as well).

using glob:

I don't quite understand (at the time of this reading) what you're suggesting.. but I will play with it a bit after I finish up here.... I need to take pre-defined files, read and replace given UNIQUE vars within them and then replace those varables on a writeout to a different file/location

I didn't think glob would do that on a unique set of different varables based on different files for EACH file

ex: file-A has $Var1 in it and file-B has $Var2 in it

need to write-out file-C with the contents of Var-A AND then write out file-D with the contents of $Var-B (while copying EXACTLY the rest of said files)

I have played with glob before..and I couldn't get it to work for this operation.. I will play with it some more

I also tried going back to using standard TCL sockets and stuff on another version.. and all those fun "hanging socket" problems are back!

I guess its time to open the textbooks on TCL...

Nah, outside of this bot, theres really no reason to learn it all over again..

I hate life sometimes! <smirk!>

-DjZ-
:) :)
n
nml375
Revered One
Posts: 2860
Joined: Fri Aug 04, 2006 2:09 pm

Post by nml375 »

The $out is not the problem, it's the other files you open in the beginning of the proc (in which you don't keep the file handle)... And since you don't keep the file handle, you can't close it...

Or, [read [open somefile r]] doesn't return a file-handle, but the file contents...
Any attempt to close whatever the above returns will fail (except for the extremely unlikely case that the whole content of the file miraculously matches the file handle assigned by tcl, don't ever expect this to occur...).

Proper way of doing things like this looks like this:

Code: Select all

set fchannel [open "thefile" r]
set data [read $fchannel]
close $fchannel
See the fundamental difference between $fchannel (file channel identifier) and $data (actual contents of the file "thefile")?


Glob:
Glob will return a list of filenames matching the pattern argument.
The way I would do a template-system with tcl would go something a little like this:

Code: Select all

proc make {} {
  foreach $template [glob "templates/*.tmpl"] {
    set tmplChan [open $template "RDONLY"]
    set htmlChan [open [file join "public_html" "[file rootname [file tail $template]].html"] "WRONLY CREAT TRUNC"]
    set tmpl ""
    while {![eof $tmplChan]} {
      append tmpl [read $tmplChan]
    }
    puts -nonewline $htmlChan [uplevel #0 [list subst -nobackslashes -nocommands $tmpl]]
    close $tmplChan
    close $htmlChan
  }
}
The above code could of course be extended to move other files than *.tmpl, do timestamp checking, and probably whatever bells'n'whistles you could think of... But for starters, it'll gather a list of files with the extension .tmpl in the templates folder (relative to from where you started your eggdrop), open each file in read-only mode, also opening a second file in write-only mode with the same name but the extension .html, in the public_html folder, read the content of the template, substitute any variables present in the data (in a global context), write the result into the file opened in public_html, and finally close both files and move on to the next one in the list.


"Hanging sockets":
This means that you are using synchronous, or blocking, sockets. As I've told you before, use asynchronous sockets and implement a trivial timeout. It's really as simple as that.
The http-package will not work well, since the authors failed to implement an asynchronous connection (though once the connection has been established, the script behaves fully asynchronous).

Edit: Removed stray ] in the last block of code.
Last edited by nml375 on Sat Nov 28, 2009 9:14 am, edited 1 time in total.
NML_375
d
dj-zath
Op
Posts: 134
Joined: Sat Nov 15, 2008 6:49 am
Contact:

Post by dj-zath »

Once again, nml.. you da man!

the moment I saw your example, I noticed what I have done wrong.. (even before your explanation!) somehow I have "lost my way" somewhere down the line... I will most-definately correct this immediately!

the glob example:

that ones a bit more to get.. though I am lookiung at the example carefully and reading your explanation.. this has gone a little past what I understand.. but I will study your example- and know/understand how it works!

again, you have been an invalable source of help and advice.. and I owe you many beers :)

-DjZ-
:) :)
d
dj-zath
Op
Posts: 134
Joined: Sat Nov 15, 2008 6:49 am
Contact:

Post by dj-zath »

nml:

once again, awesome job!

admittingly, I'm surprised I got as far as I did.. based on that last piece of code that you introduced, I wasn't aware of "file rootname" and "file tail" so you can see why I did what I did.. hmmmmm just goes to show, I guess .. "newbie" huh? :)

but I admit my wrongs.. and in doing so, I correct them :)

I plugged in your code, and with "adjustments" I have it working seemingly well.. except for an anticipated problem with the playlist.html generation.. but I'm sure I'll figure it out..

I DO have a "newbie" question..

your example has "RDONLY" where I changed to 'r' and "WRNG CREAT TRUNC" where I changed to 'w' (and I removed an extra ']' down the line as well) Now the newbie part.. Was I supposed to?? It seems that was placed there for reasons I can't understand (or, more like the case don't understand...) So I changed, there, what I think, to the "correct" sintax...

-DjZ-
:) :)
n
nml375
Revered One
Posts: 2860
Joined: Fri Aug 04, 2006 2:09 pm

Post by nml375 »

Both syntaxes are correct. I generally use this syntax, as there is no question as to what I want to do with the file. You can use the short-hand modes, as these map most access-mode combinations..

Sorry for the stray ], I assume it's the one at the end of the subst-line?
NML_375
d
dj-zath
Op
Posts: 134
Joined: Sat Nov 15, 2008 6:49 am
Contact:

Post by dj-zath »

hi there Dr. nml!
Both syntaxes are correct. I generally use this syntax, as there is no question as to what I want to do with the file. You can use the short-hand modes, as these map most access-mode combinations..

Sorry for the stray ], I assume it's the one at the end of the subst-line?
Yeah, it was the one at the end of the subst line no problem.. I found it realatively quickly..

see? I do look carefully- and, at least, try to understand what I'm doing- though I have my flaws :)

you have helped out a LOT on this project... I appreciate that :)

-DjZ-
:) :)
Post Reply