I realized I didn't make a proper announciation so I'm doing that now:
https://brlcad.org/wiki/User:Paddedto10/GSoC18/ProjectI've already written some code but my commits are local so far. (using git svn)
Thanks for the intro @Peter Pronai, looking forward to seeing your project in action! Before you make a bunch of local commits, please remember to submit patches so progress can be incrementally inspected and discussed.
Ideally, you should carve off the most simple patch(es) you can think of and submit them for review. Typically, two "perfect" patches will earn you commit rights so you can do all those commits to the actual repository.
@starseeker should I send patches of the smaller changes I have so far? it's just some typedefs/additional fields + the c_exec function is implemented, although I would like nicer error handling in it, but that would probably take some larger changes, outside of that specific function.
@Peter Pronai sure, go ahead and submit the patch if it's in a clean state
@Peter Pronai At least with GCC 8 locally here, I can now build strict with latest trunk.
@starseeker oh forgot to write this: yup, strict build now works here as well
other thing: I think I'm basically done with the librt part of things, so the next thing would be actually changing db_search to actually accept the new arguments (the exec handler and the userdata), but that would mean having to rewrite every call to it, and there seem to be a lot of them
of course none of them use -exec _now_, so it'd be fine to just pass some NULL pointers, but later that might lead to problems
alternative: I could write the TCL exec evaluation function for MGED and test it on its own, and I still need to actually deliver on my promise to do TDD but the CMake testing is quite intimidating, so I've been putting it off
so, I think I'll do a head first dive into the testing framework and set up the tests for the currently existing functionality and then I'll do another dive into the TCL API
@Peter Pronai Out of curiosity, why start with Tcl?
We need to be able to exec libged commands, which as a starting point would avoid the need to tangle with the Tcl API.
I'd also suggest prototyping a db_search function signature, so we can think about how we want to set up the API
@starseeker I already have the part that calls the callback, writing the MGED side exec handler sounded like something I could do relatively easily, but it's a lot more complicated than I thought.
well, it's not exactly complicated, more like not very well documented. I just have to call a Tcl command with a list of strings as its parameters.
but idk which API calls are relevant
anyways, I think I'll do tests instead
@starseeker is calling libged functions directly actually needed? since they get registered as TCL commands, it makes more sense to me to just call them from TCL. That also makes things like calling TCL builtins more straightforward.
TCL is (eventually) going to be pushed out of the lower level libraries (including libged) and will live only in libtclcad and above. So TCL shouldn't be necessary to the core functioning of the system.
@Peter Pronai Do you have a "working" -exec option to search? If so, what does your new db_search function callback look like?
@Peter Pronai You'll need to be able to pass a function callback into db_search and execute it - how are you thinking to set this up? It shouldn't be Tcl specific
@starseeker not yet, I'm doing one of the parts I've been putting off: changing the function signatures so I can pass the callback and the userdata around. It's almost done, just one definition left to change.
I also looked into where libged's "commands" are used and it does seem to me like they are only used in MGED.
I postponed the MGED side callback because I thought it will be better if I can test it properly with db_search. Testing the function on its own, without db_search being done, would have meant having to figure out a bunch of build system things and that would have just been wasted time.
@starseeker before I sent my proposal we agreed with @Sean on the following:
callback function takes an int argc, char *argv[], and a void *userdata
the userdata in MGED's case is the Tcl interpeter pointer
@Peter Pronai OK, so the argv array is the path list from the search run?
the argv is what the command is called with, the '{}' get replaced with the current node's path
I think this is as abstract as it can usefully get. It supports every argc/argv kind of command reasonable.
OK, so the -exec "template" will be populated on a per-search-path basis and the function will be execed separately on each path?
Yes, exacly. Can't really do it any other way as far as I know.
something like: search / -name "a*.r" -exec "myfunc --special-option1 --special-option2 {}"
and a ';' at the end
oh wait. nah, nvm, that's only in the search syntax
libged then uses a function table to look up the function in question (translating "myfunc" into an actual function) and the options and {} are assembled on a per path basis as the arguments to that function.
it doesn't get passed.
libged is not touched directly, Tcl already has the functions registered so it is left up to Tcl to call them
otherwise it wouldn't be able to handle:
-renamed functions
-Tcl builtins
That might work for a first cut, but libged will eventually not have any direct knowledge of Tcl
MGED (or libtclcad) will need to register a callback for handling Tcl processing with libged
hmmm, can you give an example where it won't work?
the current one, I mean.
For now, as long as any command you want to run is known to the Tcl command execution environment, that should work. But my point is that eventually we don't want to assume Tcl in order to run commands. Let's say someone makes a Python version of MGED. Or they want to link to libged and construct their own search commands programattically, then register their own custom exec function callbacks with libged and run them on the search results.
You can start with Tcl execing in order to get something going for testing, but once that is demonstrated we need to think about how to generalize things.
(the latter scenario in particular - linking to libged and supplying customer defined exec functions to run - is fairly likely to crop up eventually)
Hmm, I guess if it's Python, we would have to indicate which parameter is the path, so that it is presented as the right type.
I guess if the customer links to libged directly, we can provide a pre-made callback that simply searches for commands in the command table and calls them directly. That's even easier than the Tcl evaling.
Well, if the function callback hook is a generic int my_exec_func(int argc, const char **argv, void *userdata) and the exec string supplied to the -exec search option is broken out into the argv input passed to my_exec_func (after replacing {} with one path) then the only questions are how to match the string "my_exec_func" in the search -exec function to the actual function pointer to call and how to pass all that around
oh, the callback is not the one that is matched, the callback does the matching
right - we need some way (probably in libged) to define a language agnostic "string to exec function pointer" mapping that can be populated, and then either pass that map to the db_search call so it can figure out how to do the exec there, or have libged do the string to pointer breakdown ahead of time and pass the actual exec pointer to db_search
if you're almost there with a design, go ahead and finish and we can use that as a basis for further discussion
ok, I'll fix this part that I'm working on then call it a day
I guess we could make the callback function find the "actual" function that will do the work... not sure about that one.
sounds good.
also,seeing how I've changed db_search's prototype, there will be a lot of broken calls to it
don't worry about that too much yet - let's see how you're thinking about changing it before you go too far.
yeah, better stay focused
btw I have my code up on gitlab if you wanna take a look:
https://gitlab.com/raingloom/brlcad/
the relevant branch is "gsoc-search-exec"
anyways, back to work
@starseeker so, I'm trying to fit the exec specific parameters into ged_search but it breaks the command tab, so I'm thinking about adding it to struct ged
. Is that ok? Is this supposed to be thread safe and stuff?
command tab?
this thing: src/libtclcad/tclcad_obj.c:989
it depends on all the functions having the same prototype
@starseeker so, I'm trying to fit the exec specific parameters into ged_search but it breaks the command tab, so I'm thinking about adding it to
struct ged
. Is that ok? Is this supposed to be thread safe and stuff?
many commands have custom command hooks, so you shouldn't need to modify the struct ged...
@Peter Pronai I think the way that's going to have to be handled is to have the calling code "wrap up" the to_cmds information into one container struct that can be passed through the generic exec function, and then have an "unpack and run" function that is the actual hook function supplied to search exec for the Tcl commands. In essence, the unpack function will serve as the dispatcher to the to_cmd interface.
basically, "unpack" will turn the argv string exec generates and the contents of the data structure into a to_cmd call.
@starseeker where would the client entry point(s) be? There is a to_open_tcl
that seems to allow for some userdata.
I'm not following - what do you mean by client entry points?
I think the way that's going to have to be handled is to have the calling code "wrap up" the to_cmds information
I'm trying to figure out where the client code would do this and which info to wrap
Ah. @Sean , what would be the "right" way to pass application client data through a libged command call?
@Peter Pronai in this particular case you could probably use the ged_interp pointer in struct ged for testing purposes - it's not intended to be relied upon (at least, not in that form) but right now I believe that's holding the Tcl interpreter used by the libged commands working with Tcl.
So a libged db_search call could pass the gedp->ged_interp pointer through the call with a void cast, and then your "run_to_cmd" function would cast it back to a Tcl_Interp before calling the actual to_cmd.
Probably the ged_interp should morph into a generic "userdata" pointer
struct ged is defined in include/ged/defines.h
@starseeker thanks! Gonna try that.
Ah. @Sean , what would be the "right" way to pass application client data through a libged command call?
the only way for a librt function to call into libged state without creating an architecture cycle or violating modularity is for librt to take a function pointer AND a data pointer (which both get passed to the function). qsort() is a quintessential example of this.
there are common ways for doing this statefully (e.g., context = db_search_register_callback(mycallback); db_search(context, ...); // data passed internally or explicitly) or stateless (e.g., db_search(mycallback, ...); // data passed internally or explicitly).
since you're talking about an interpreter, it likely needs to be explicit
which in that pattern could be as simple as a separate db_search_register_data() call
so with a stateful context, something like ctx = db_search_context_create(); db_search_register_exec(ctx, callback); db_search_register_data(ctx, interp);
@Peter Pronai How are things going?
@starseeker the last few days were a bit hectic so I'm catching up with the coding today, I just finished modifying librt to use a single context struct pointer instead of separate userdata and callback parameters
right now i'm building mged to see where it broke and where i have to change things in libged
@starseeker ok, so I have some basic eval thing working, specifically this:
% echo 'search * -exec echo yay ";"' | ./bin/mged ../../db/axis.g yay
produces some output without crashing
but I think it should be printing that multiple times?? not sure what's going on on the Tcl side, I should probably look at existing scripts for commands I should try.
I tried -exec true ";"
but it gives an "invalid command name: true" in the log....but still prints every item.....
oh, of course, this is not a C main, 0 is abnormal return here
-exec echo "{}" ";" seems to do the right thing now but it only printed one path, no idea why. but it returned true for everything so exec printed them all
That looks like invalid find/search syntax.. asterisk?
Might I suggest running in immediate mode: mged f.g obj search . -exec echo yay \;
That way you know exactly what the argc/argv should be.
@Sean the -exec true and the current -exec echo I'm trying are without the asterisk
i know that the f_exec in the plan is evaluated the right number of times so it must be something else messing up
this is what echo 'search -exec echo "{}" ";"' | ./bin/mged ../../db/axis.g
does:
/axis/x.r/x X1 X2 X X.r ...bunch more names
@starseeker ok so I think I know what's going on and it's probably not my code that's wrong
I've noticed that many parts of the code actually "print" into memory, the echo output I see there is the last node in the db that librt looked at, so what's probably happening is:
echo
writes to that bufferecho
overwrites that buffer when it's called againecho
should be appending, not overwritinganyways, I guess that's the core functionality done??
ah. apparently it might not be broken, just.... really, really weird and counterintuitive
one would think that echo
prints to stdout, right? but no, there is a result field in Tcl_Interp
I guess that should be printed after every Tcl_Eval??? this is weird, why would somebody want this??? can't this be done with pipes???
@starseeker well, I added an extra puts()
call so now the eval function I wrote takes care of printing, but it still comes after the Tcl output (in this case: the list of found items) unless I explicitly fflush(stdout)
but yeah, that's... about it??? should I send a patch so others can try it as well?
what I think _could_ be done:
the context is currently the last parameter, but leaving the database pointer to be last might be better? it would be pretty easy to switch their order.
BSD find has the -ok
variant, which asks before execution, is this needed?
then there is the "{}" "+" version, which adds things at the end. that's just a minor change in the parser
also, find(1)
does not have the implicit -print
when -exec
or -ok
are present, leaving printing up to them
however, librt does not actually print, it just returns an array and the interpeter has no easy way to do that, so I think this should be left out to keep things simple??? or the presence of -exec
should be communicated towards the client code, so that it can decide if it wants to do implicit printing
oh and of course existing code outside mged that uses db_search beeds to be updated
it's not broken -- tcl does exactly what we tell it to do so if the behavior isn't right, it's pretty much on us -- and how it's been set up in mged/libged
there is a result field and most commands call Tcl_AppendResult to add to it. some commands clear the result before printing to start fresh, some merely append, some return a partial result and tell tcl more will come later
yeah, figured it out later, but damn that was frustrating. i'm used to I/O working on the standard files, so this was very weird.
so, what should I do with the remaining thingies? what to implement? what to change?
I'd get things working across the system first (update all the db_search calls, add documentation to the man page for search including syntax documentation and examples)
what is the new db_search function signature?
definitely put together a patch so other people can try it out
@starseeker submitted patch on SF
the sig is the same except there is a ctx
parameter at the end
@starseeker ok, got it right this time, the Tcl result is now properly interpreted
I only tested it with search -exec echo yes ";"
and search -exec echo no ";"
so far, but those work.
@Peter Pronai your latest patch file on sourceforge is empty?
...wha... oh hell.
a sec, gonna regenerate it
np
well this is... odd... git diff master
works but git diff master -- <some files I took from the diff output>
produces an empty output
ok, i uploaded it again
OK, one thing I see right away - you're allocating the db_search_context with bu_malloc, but calling free on it rather than bu_free. I'd recommend for this situation, if you're going to have a db_search_context_create function, you also add a db_search_context_destroy to handle the deallocation.
minor point - mged_db_search_callback has some trailing whitespace in it, which we generally try to avoid.
For vim, I use this trick to highlight it: https://stackoverflow.com/questions/4617059/showing-trailing-spaces-in-vim
Not sure what other editors do... Sean can probably advise on Emacs
I uh, use Acme. >_>
heh. maybe this? (untested): https://gist.github.com/echosa/a5fd4b6637718c455e43
I'll probably just run sed -i
over them :shrug:
that way I don't even have to open the files
that works too
@Peter Pronai did you get a chance to update the patch?
@Peter Pronai if you have it all working, check out the manual page for search (brlman search) and see which of the examples it has at the end can turn into shorter search -exec examples. it's also a good test set in general to make sure everything is working right.
@starseeker I changed the calls to bu_free but I haven't uploaded it yet, I will in a bit
@Sean thanks, I'll check those (and add some examples with -exec)
bu_free complains at runtime about freeing a null pointer, but afaik free(0)
is supposed to be a no-op??? does this need an extra check?
Erm. @Sean , is bu_free supposed to be OK in that situation?
i'm trying to do the examples in the manpage but uhh, i'm getting errors and since Tcl is so amazing, it doesn't even tell me where the error comes from
like... this: echo [expr {int(rand()*225)+30}]
extra characters after close-brace
:cry:
and when it doesn't error it just prints it as a string
uhhggjhlkhl
i'm reading a tutorial but so far it's been about the standard library
and that's a reduced example, the one in the manpage was bigger and gave the same error
well. i didn't run it directly so maybe this is some weird context sensitive thing
also, man search
doesn't work in -c
mode apparently? it says man: no manual page /home/rain/Sync/gsoc/build/debug/share/man/mann/search.nged
but that exact file is Right There :angry:
uhhh. ok. so it works in the original script but as soon as i try to make the loop body into a function with proc
it starts complaining when i call the function
....huh. well it works now....
idk what i did differently....
well. nevermind i guess.
uh turns out the full paths i was printing can't actually be used??? this is.... weird.
ok, made another example work with -exec
damn.
global variables affecting seemingly basic commands..... yikes
also now f_exec doesn't use full paths because it turns out that was just some debugging thing maybe? because the Tcl interpeter wanted nothing to do with them
@Peter Pronai There are some commands that will use full paths - the example that comes to mind is arced, although I don't think I have a scripted example of using it handy
"brlman arced" from the command or "man arced" from the MGED command line should give you the arced man page - I'd try a few experiments with it and then see if you can roll it into a search -exec example successfully
that's quite confusing, i thought paths worked the same way they do in a shell
is there any reason for the differences?
well, arced doesn't work with the short paths
but others don't work with the long ones
hmm
r part1.r u rcc2.s – sph2.s
doesn't work in the classic ui
That's OK (commands only working with obj or full path respectively) as long as the commands in question do work when given the correct inputs
What's the error?
i think it was "wrong number of arguments sph2.s"
it went away when i quoted the "-"
mged> r foo.r u rcc2.s – sph2.s sph2.s error in number of args! mged>
oh. nvm the quoted one also errors
mged> r foo.r "u" rcc2.s "–" sph2.s " sph2.s Defaulting item number to 1002 Creating region with attrs: region_id=1001, los=100, material_id=1 bad operation: (0x0) skip member: sph2.s
@starseeker just throwing this out there: why not have a filter command? functional style? can Tcl do that?
although... that would be language dependent... hm.
I separated the -exec option into an -execs
and and -execf
one, the former uses the short name and the latter uses the full path.
but full paths - as emitted by db_path_to_string
- also don't work
That might be the leading slash?
@Sean What do you think is the "right" thing to do here? Teach commands that use a path to handle the search output paths, have exec "dumb down" the paths somehow for feeding to libged, ...?
@Peter Pronai I'm not sure we're going to want to have two different exec options - that's OK for testing, but I think we'll want to resolve some sort of sane default behavior for all commands that doesn't require user awareness of that.
@Peter Pronai what does "draw" do with full paths? IIRC Bob updated that some years ago to be able to handle full paths, but there again that lead slash might be an issue...
From a command standpoint, it might be as simple as ensuring sane failure behavior if the "wrong" form of the path (short vs. long) is supplied - then an exec with the wrong search path modifier will just not do anything, and the "fix" is to change the path modifier to produce the correct form for the exec in question.
Of course, there's also the situation with multiple exec calls where some expect short paths and some are looking for long paths.
Part of me almost wants to audit the commands and add (where appropriate) the ability to each command to handle full path inputs by "pre-processing" it down to the leaf object, if that's what the command is looking for.
db_string_to_path -> DB_FULL_PATH_CUR_DIR -> dp->d_namep for each string supplied as a "geometry" object to commands that don't work with full paths, or something along those lines...
I'm definitely much more inclined to fix the commands to do the "right" thing than I am to contort search-exec to try to "feed" them properly dumb-ed down inputs
free(0) is fine per the standard but is typically wrong or lazy memory management (often the prior), so bu will blather a warning expecting you to make sure the caller knows how much was allocated, how much will get freed, etc.
For the r command, I believe your expression is wrong. Don’t put the first op (u).
c, r, comb, and g all take different types of expressions
@starseeker in that case I'll go through the commands and see which ones are doing weird things
come to think of it.... the current behaviour might be ok, since the user could just concatenate the short path with some prefix
i'm trying to figure out how but the Tcl concat command adds spaces between things
i think this can only be done with a helper function?
so this sort of works: search Default -depth 1 -execs regsub bloop Default/bloop "{}" ";"
what if instead of expecting a two part "path", arced just took two parameters?
found another that takes paths: listeval
that one pretty much only makes sense with paths
@Peter Pronai my sense is that we should come up with one or two path processing functions that will "digest" full path and object names and determine what was supplied, then have commands either reject the input (path command given non-path object, for example) or "do the right thing" (e.g. pull the leaf object from a full path and send that forward to the command.)
Updating all the commands to be robust in that regard will be something of a slog, but we can at least establish the correct patterns.
there needs to be a general object pathname processing, but that will require at least a case-by-case evaluation. anything that takes an object name should take a pathname, and anything that takes a object name implicitly refers to a path. fortunately, nearly all commands will easily update to supporting either. the ones that don't will have to be considered separately (e.g., maybe we decide to eliminate or generalize the outliers).
come to think of it.... the current behaviour might be ok, since the user could just concatenate the short path with some prefix
the 'join' command is the typical way to do this with tcl
so, i have a function like db_lookup
that uses paths, it's only missing the part where it checks if the path components are actually connected
now, should it count any connection or should it be restricted somehow?
...where in hecc is the function that lists connected nodes......
so, i have a function like
db_lookup
that uses paths, it's only missing the part where it checks if the path components are actually connected
@Peter Pronai can you explain or show this please? we don't want multiple lookup functions if we don't need to have them. adding support for paths to db_lookup() would seem entirely reasonable if it doesn't support them already. it would naturally need to fail lookup if the path was invalid.
now, should it count any connection or should it be restricted somehow?
not sure what you mean here
global variables affecting seemingly basic commands..... yikes
Eliminate them! ;)
I separated the -exec option into an
-execs
and and-execf
one, the former uses the short name and the latter uses the full path.
I don't think this is the right thing to do here. whether short or full paths should replace {} is already implied via search . vs search / ... there should just be one -exec option regardless.
but full paths - as emitted by
db_path_to_string
- also don't work
what did you mean by this? invalid paths? problems with commands that don't take paths?
problems with commands that don't take paths and i think the ones that do take paths don't take absolute ones
the function is the following:
struct directory * db_lookup_path(const struct db_i *dbip, const char *_path, int noisy) { struct directory *dir = RT_DIR_NULL; size_t i=0; size_t j=0; char tmp; char *path = bu_strdup(_path); /* skip leading slashes */ while (path[i] == '/' && path[i] != '\0') i++; j=i; while (path[j] != '/' && path[j] != '\0') j++; tmp = path[j]; path[j] = '\0'; /* start from the "root" */ dir = db_lookup(dbip, path + i, noisy); if (dir == RT_DIR_NULL) goto exit; for(;;) {/* FIXME: figure out a better loop condition than `true`?? */ path[j] = tmp; if (path[j] == '\0') /* reached the last segment */ goto exit; i = j + 1; while (path[i] == '/' && path[i] != '\0') i++; j = i; while (path[j] != '/' && path[j] != '\0') j++; tmp = path[j]; path[j] = '\0'; if (1) { /* path segment connects to dir */ dir = db_lookup(dbip, path + i, noisy); } else { bu_log("malformed path"); /* FIXME: write more descriptive message once the connection check is implemented */ return RT_DIR_NULL; } } exit: bu_free(path, "mutable path"); return dir; }
it builds on db_lookup
and could replace it if necessary
the connection thing: should it eg. only go down unions but not intersections?
i think it should consider any connection a valid one and it any filtering is necessary, that could be added later
there were some unexpected pet related troubles at home so i didn't have a lot of time to code :/
tomorrow we're going on that vacation so i should have ample time to code and read more of the source in the car
it builds on
db_lookup
and could replace it if necessary
this likely makes more sense. existing db_lookup could be made a HIDDEN/static function renamed to just lookup_object() or something similarly simple, and your new one taking over as db_lookup() assuming the signature matches. it would need to match.
the connection thing: should it eg. only go down unions but not intersections?
i think it should consider any connection a valid one and it any filtering is necessary, that could be added later
Ah, no, I don't think it should be arbitrarily limited by the operation. Stopping doesn't make sense for intersections in particular. The only real difficulty is that there can be multiple objects at a given point in the path (e.g., /path/to/foo/bar might have 'bar' in 'foo' N times, with different matrices on each one). That is only really solved by switching from paths to URI's, and I think that's outside scope for this project.
@Peter Pronai Peter, can you do another version of your patch that doesn't have extra whitespace in it? I don't remember if we say it explicitly in HACKING, but generally we try to avoid trailing whitespace on lines.
@starseeker sure, i'll send a fixed one after i'm finished with this function
well.. i have something that should check connections, but it seems like it just considers everything connected...? that took a lot longer than i hoped for and it still doesn't work. frick.
Can you show your code? Maybe another pair of eyes
HIDDEN int connected_directories(const struct db_i *dbip, const struct directory *dpa, const struct directory *dpb) { int ret = 0xdead; struct directory *dpachild; size_t i; struct rt_db_internal interna, internb; struct rt_comb_internal *comba; size_t true_count, trueish_count; struct rt_tree_array *rt_tree_array = NULL; if (RT_DIR_NULL == dpa || RT_DIR_NULL == dpb) { /* FIXME: panic maybe instead??????? */ ret = 0; } else if (!(dpa->d_flags & RT_DIR_COMB)) { ret = 0; /* not a combination, so paths can't start from it */ } else { if (!(dpa->d_flags & RT_DIR_COMB)) { ret = 0; } else if (rt_db_get_internal(&interna, dpa, dbip, (fastf_t *)NULL, &rt_uniresource) < 0 || rt_db_get_internal(&internb, dpb, dbip, (fastf_t *)NULL, &rt_uniresource) < 0) { bu_log("Database read error, aborting"); ret = 0; } else { comba = (struct rt_comb_internal *)interna.idb_ptr; if (comba->tree) { if (db_ck_v4gift_tree(comba->tree) < 0) { db_non_union_push(comba->tree, &rt_uniresource); if (db_ck_v4gift_tree(comba->tree) < 0) { bu_log("Cannot flatten tree for listing"); ret = 0; goto exit; } } trueish_count = db_tree_nleaves(comba->tree); if (trueish_count > 0) { rt_tree_array = (struct rt_tree_array *)bu_calloc(trueish_count,sizeof(struct rt_tree_array), "tree list"); true_count = (struct rt_tree_array *)db_flatten_tree( rt_tree_array, comba->tree, OP_UNION, 1, &rt_uniresource) - rt_tree_array; BU_ASSERT(true_count == trueish_count); comba->tree = TREE_NULL; } else { true_count = 0; rt_tree_array = NULL; } for (i = 0; i < true_count && !ret; i++) { dpachild = db_lookup(dbip, rt_tree_array[i].tl_tree->tr_l.tl_name, LOOKUP_QUIET); if (RT_DIR_NULL != dpachild && 0 == bu_strcmp(dpachild->d_namep, dpb->d_namep)) { ret = 1; } db_free_tree(rt_tree_array[i].tl_tree, &rt_uniresource); } while (i < true_count) { db_free_tree(rt_tree_array[i].tl_tree, &rt_uniresource); i++; } if (rt_tree_array) bu_free((char *)rt_tree_array, "printnode: rt_tree_array"); } else { ret = 0; } } } exit: rt_db_free_internal(&interna); rt_db_free_internal(&internb); BU_ASSERT(ret != 0xdead); return ret; } HIDDEN int connected_path(const struct db_i *dbip, const struct db_full_path *fp) { size_t i; const struct directory *dpa, *dpb; switch (fp->fp_len) { case 0: case 1: return 1; default: dpb = fp->fp_names[0]; if (RT_DIR_NULL == dpb) return 0; for (i=1; i<fp->fp_len-1;i++) { dpa = dpb; dpb = fp->fp_names[i+1]; if (! connected_directories(dbip, dpa, dpb)) return 0; } } return 1; }
i couldn't find a way to check the connections starting from a struct directory
so i hoped that the tree
command's implementation will have something, that right there is a shameless plagiary of _ged_print_node
maybe the problem is that i'm testing it with the wrong file? I've been doing all my tests with the supplied NIST_MBE_PMI_7-10.g since it's pretty big so i thought it should be complex enough. maybe the output of tree
just doesn't reflect the connections the way they really are?
the tests that i expected to fail were things like
ls Default/Default
or ls Solid
.... oh..... OH!!!!!!!!!!!!!!!!
.....wait
@Peter Pronai that's a big file, but it's not a very complex database .. it's just got some big nurbs objects in it. the havoc or m35 models are considerably more complex
Default/Default?? ... that doesn't look right :)
i know! and it works! and it shouldn't!
and ya, i later realized it's pretty simple but i couldn't remember which one the big complex tank was. >_>
and the NIST one was fine for just testing if search
worked at all
we don't have a big complex tank in our samples
the "goliath.g" model kind of looks like a tank but is actually a tracked mine ... like a land mine that explodes, but it has tracks on it so it can be driven around
it must have been that then. but anyways, Default/Default shouldn't work and it's not just some quirk of the database in question.
maybe ls doesn't use db_lookup....? uhh.
i guess i can't avoid it. i'll have to step through this with gdb.
maybe ls doesn't use db_lookup....? uhh.
i guess i can't avoid it. i'll have to step through this with gdb.
it does use db_lookup (see src/libged/list.c) but is also path-aware if there's a slash -- there may be some case where object/object resolves wierd
oh.... i think i have it working....
well, cat Default/Default
no longer works
(cat turned out simpler than ls so i'm testing it with that)
it was an off-by-one error, naturally
cat Default/Solid1.s
also doesn't work
but it doesn't display an error either... it probably should
cat Default/Solid1
does work
also, absolute path syntax works now, so search
should work too
this indentation style is not a good fit for acme :(
i think i'll write some scripts to convert between the two, so I can work purely with tabs but then I can convert things back. i've tried the official formatter script but it works on files and I want to work with selections as well and that's easier done with a few sed
invocations
i think there already is something like that. check out sh/ws.sh
it looks like it works on files and not stdin... ah well, let's try this
found something simpler:
# convert from the weird indentation to something simpler |sed 's/ / /g s/ / /g' # revert the previous conversion |sed 's/ / /g s/ / /g s/ / /g'
it seems to work and i didn't have to learn perl to know what it's doing :3
this is also nicer because it's easy to apply to selections in Acme so it's easier to track what I'm changing
(cat turned out simpler than ls so i'm testing it with that)
yes, 'cat' is basically a nearly direct call to the ft_describe() callback
it also calls db_lookup() itself, but without modifying the arguments
we have plans to convert the entire codebase indentation over. that was waiting on repo migration, but it might make more sense to do it first now that migration is stalled...
@Peter Pronai So on the patches, please separate them out by topic (you've got the older patch with color, edcolor, prcolor changes which I don't (think?) are related to search -exec)
I'm thinking there are three here - the color stuff, the initial search -exec patch, and the follow-up that does the db_lookup changes
@Peter Pronai what's the "something weird" when you include the type from librt in ged/defines.h?
I think I have separated the prcolor stuff out?? The something weird was some compilation error, but I can't remember what exactly.
But yea, there are changes in my branch that aren't directly related and that's probably not good because it's hard to clean up the diff from the master branch.
problem is idk how to cleanly separate those changes and some of them are nice to have..... maybe i could make each change a new feature branch and make one private branch that has all those changes????? idk
@starseeker
In file included from /home/rain/Sync/gsoc/brlcad-code/include/ged/defines.h:37, from /home/rain/Sync/gsoc/brlcad-code/include/analyze.h:34, from /home/rain/Sync/gsoc/brlcad-code/src/libanalyze/api.c:28: /home/rain/Sync/gsoc/brlcad-code/include/rt/search.h:141:39: error: ‘struct db_i’ declared inside parameter list will not be visible outside of this definition or declaration [-Werror] struct db_i *dbip, ^~~~ /home/rain/Sync/gsoc/brlcad-code/include/rt/search.h:140:39: error: ‘struct directory’ declared inside parameter list will not be visible outside of this definition or declaration [-Werror] struct directory **path_v, ^~~~~~~~~ /home/rain/Sync/gsoc/brlcad-code/include/rt/search.h:178:38: error: ‘struct directory’ declared inside parameter list will not be visible outside of this definition or declaration [-Werror] struct directory ***dpv); ^~~~~~~~~ /home/rain/Sync/gsoc/brlcad-code/include/rt/search.h:175:44: error: ‘struct db_i’ declared inside parameter list will not be visible outside of this definition or declaration [-Werror] RT_EXPORT extern size_t db_ls(const struct db_i *dbip, ^~~~ /home/rain/Sync/gsoc/brlcad-code/include/rt/search.h:203:91: error: ‘struct db_i’ declared inside parameter list will not be visible outside of this definition or declaration [-Werror] DEPRECATED RT_EXPORT extern int db_full_path_list_add(const char *path, int local, struct db_i *dbip, struct db_full_path_list *path_list); ^~~~ /home/rain/Sync/gsoc/brlcad-code/include/rt/search.h:205:74: error: ‘struct db_i’ declared inside parameter list will not be visible outside of this definition or declaration [-Werror] DEPRECATED RT_EXPORT extern void *db_search_formplan(char **argv, struct db_i *UNUSED(dbip), struct db_search_context *); ^~~~ /home/rain/Sync/gsoc/brlcad-code/include/rt/search.h:209:20: error: ‘struct db_i’ declared inside parameter list will not be visible outside of this definition or declaration [-Werror] struct db_i *dbip); ^~~~ /home/rain/Sync/gsoc/brlcad-code/include/rt/search.h:212:21: error: ‘struct db_i’ declared inside parameter list will not be visible outside of this definition or declaration [-Werror] struct db_i *dbip); ^~~~ /home/rain/Sync/gsoc/brlcad-code/include/rt/search.h:215:49: error: ‘struct db_i’ declared inside parameter list will not be visible outside of this definition or declaration [-Werror] struct db_i *dbip, ^~~~ cc1: all warnings being treated as errors make[3]: *** [src/libanalyze/CMakeFiles/libanalyze-obj.dir/build.make:63: src/libanalyze/CMakeFiles/libanalyze-obj.dir/api.c.o] Error 1 make: exit 2 make[2]: *** [CMakeFiles/Makefile2:18004: src/libanalyze/CMakeFiles/libanalyze-obj.dir/all] Error 2 make[1]: *** [CMakeFiles/Makefile2:18278: src/libged/CMakeFiles/libged.dir/rule] Error 2 make: *** [Makefile:4422: libged] Error 2
this is what happens if i include rt/search.h
#include "common.h" #include "bio.h" #include "bu/hash.h" #include "bu/list.h" #include "bu/vls.h" #include "dm/bview.h" #include "rt/search.h"
this is the include order
OK, so there are two ways to handle that. For simplicity, I would suggest also including the header that defines db_i (rt/ db_instance.h) in your C file along with rt/search.h
i included it in rt/search.h itself, since that's the one that seems to depend on it
@Peter Pronai That was my initial thought, and if that works without causing additional problems it's the right thing to do. There are sufficient interdependence between the rt datatypes I wasn't sure if that would "just work", but if it does awesome.
well... i rebased my branch (well, a copy of it)
i squashed a lot of thing into one and left out some commits and git diff tells me it's the same as the other branch
so, gonna send the new and improved patches
(editing the patches by hand was just. so. slow.)
(so i really hope this works)
uh.... what. i can't see the squashed commits... it's the same commits as on the original branch.... what....
and it has all the unrelated commits too
but.... i thought the rebase ran successfully??
well. this is irritating.
hm.
...
is the big patch really that bad? ehehehehehe.....
i can remove the unrelated fixes from it but i don't think i can separate it properly
hmmm
so, i did some git magic and came up with this:
+ b9bae437bb4cdf5fb4cf054ae761a2139f5477c8 added exec userdata pointer to struct db_plan_t + 01a04b0a52c9ac93f275a01961c41bdb13dd8a6c remove shorthand definitions from librt search + 499291ac5965258379e0cd3988d23f4fa73dcb60 basics of -exec flag for librt search + f277ef7afc64bdc28c24c35c01d4356854d18363 clean up memory on -exec parse error in librt + 9ff5cc25a6f618c957695553c79d20aa7593a9b5 librt: store result of possibly failed plan creation + e99b62b433c127d024ef5cddcdebb593a0aa6c89 print newline after bu_log in librt's c_exec + 7906e5864600773889ff771c63ca47dba2d92f8b oops. BRLCAD_OK is not 0, fixes previous commit + 14c64d7005b8e9fc9e3fd2669e618d54b5dd9632 remove debug logging from librt c_exec + 907bb124c83c9c081f42649efb6e778cc3c4f3a7 add db_search -exec related definitions + 40d385ad9ef257cbd5996e8719b58760d3afe492 follow naming convention in search.h + abfe9d6694080a2fa8a96a70bd07f21760d15889 librt/search.h: use a single argc instead of storing every argv strings's length + 767cd544c3d8547f4813ade9438f861cb6ae2078 small consistency fixes in c_exec + e5a969cdd67fd1047c6be8391b3b290ae2067e69 removed e_orig from c_exec and added a list of holes instead + 8856329eced85fc8b05c599da8b90fc6b6c7b980 first f_exec implementation + cleaned up c_exec a bit more + 2f906da457d984a567ad1e3fffdbe350bcbeb7f2 db_search -exec evaluator implemented + 188dd2e65fd71a97a1b154ab3129f58914b8f06d db_search: special case freeing db_plan_t if it's an -exec plan + 5ab4a23b53c2fba63102c67472adefa70c834af1 remove unnecessary _e_orig field from db_plan_t + ca05439929846dd40df2dcd739146b2d9a83b7a1 fixed some typos in db_search_free_plan + 9aeda60e227fc3ff1546d9e23582861058fbe7b4 make the db_search callback a const + e9889a108a34b472d3b7927021dacd3d525f1fdd hooked up the search related parameters in librts/search.{c,h} + 5af4f8ddc17da6d9ca5b6f2148a6dd397b537b31 ironed out some conflicting definitions in librt search + 0a7c1f6c18b7dfe57f7eeeefc4bf29be5c6bb8c0 added exec context definitions to rt/search.h + c19eb86b830656051d59438d811c9db56def48e9 actually, we already have NULL. removing null search context + 609ee66dbe3323b2e590de74b97b4fddb3d85907 use search context pointer everywhere instead of explicit params + 33e86bca1442ed789889333d4b942ef8bcd88a94 added NULL contexts for calls to db_search calls in libged/comb.c + 3c054b158de853919076abff96a83e3bb531e85c more NULL contexts for db_search calls in libged + 5bbc048808224fe9a4a16de3e29aaff7c3c18392 libged builds and should now support db_search contexts + ffe0b47290b62c89853db71313ece79d6e8adc47 added callback to MGED that evaluates Tcl with search -exec + fedec08c6265a0813e6e4e93224a76dce4330177 set up the callback + 04f8450e1848317ed9b6be753f3f97cb1934fa7d fixed existing calls to db_search + bba04a19849ad7c0d3a674c7a5a6a858755e0a57 improved docs for `search -exec` + 722b636913a42ed1d588974e8061f41e4a9f89e7 fixed doxygen + 66f4ce1b3bd449335f8b6159ca88da97a916e9c6 free search context after use + d30fdd961456142e952aa233494c516f8ca4b2be properly convert Tcl result to a boolean in MGED search callback + 718337152bcd6bdc8eb1b67efb2c8d341d804856 cleaned up some whitespace, probably + f79a9cc60d79993bf92e3a29099a3b6ff3063dc1 added db_search_context_destroy + 66bc37db6e1f350a5a4e2a9659a4af2d51e79d3b empty exec is a no-op that returns true + 0605f14e89c6b37f98822958781c63111a1a76c5 use bu_free in librt/search.c + 4b4e2fa52fba353d0350b647425537c260d867f4 make sure to use bu_free everywhere + fa2785ac87bc532a4f1da0f2f1c8bf0481dab21c fixed a bu_free call + 3081323a0ecf4aee443dd45c49ac07b33505e692 make the bu_free call's comment match the bu_realloc call (i thought i already committed this???) + 3859676ece1231779df7ac6bb59fe312ead44779 made some Tcl search examples work with -exec + 572f3591bd9c0f5d9f2d7ce853ade247e232a600 added a super simple and WRONG! path resolver + a597022aa8313cbb4c1c863c1dedd8141e4359da db_lookup works on paths too and there are name and path variants also + 7b0dd3cc057bff4029c850a256864396a879dedc fixed small grammatical error + e7233d774cdef630dd615f4d2c2b09cb66ecffaf removed lying comment + 442c3d883531868aa2f2ff5bd3e04e1043b0b453 i really miss rust's type system. + 7a6b2221d34457a190d4909b3af044653b7c6acc fixed bugs in the new db_lookup_path functionality + c33949237eaedf0a0125b539b7e88160ebf35394 removed trailing whitespace from db_lookup + 947fcb6d2328488a0d0752b1672604bf8bc787cc better and more error messages in db_lookup_path + 0fa85d60c05c4bef0d22f98a0bb3ab4f7b4d57ac free up the fullpath in db_lookup_path + 59c6b7aaa999d37894ff47e3ecd23fa01f96035e removed trailing ws from src/librt/search.c + caf96e4fa3bfd89629f55dc2ee1368b3a0e5c37a adhere to whitespace standards in src/librt/search.c + 90897eaf938b59fc55841012f954a502ffa81ac5 trim trailing ws from include/rt/search.h + 7719410a045e0b93b65ffa900823d9aea5dce67c trim trailing ws from src/libged/shape_recognition.cpp + 92ee7dcc7402276e36a7abad1be74fb9cd59ccab trim trailing ws from src/mged/cmd.c + 83865fc8df2f41dbe25dd659ca006c80e9964391 trim trailing ws from src/libged/search.c + 026346c7c31a5283a91a3c88ec056b9c6e8580d0 add missing db_instance dependency to include/rt/search.h + 20566236f31470e48e873cfd2e46306aac829898 removed leftover comment
these are all the commits that are actually related to the task
my branch had some miscellaneous bugfixes and this filtered them out
now, some of the commit names are wrong because i accidentally added some files or something and noticed it too late and i'd have to do another rebase to fix that
but since only i use the repo, i'll probably do it
i think i can actually skip squashing and other rebase related uglinesses and just git diff to compare commits, that way i can generate some more meaningful diffs that i can submit
Whatever you think best - the key point is to get a set of patches in shape for committing
i included it in rt/search.h itself, since that's the one that seems to depend on it
that is the right solution -- headers should be self-contained and make sure all the types they use are properly declared by including the headers that declare them. the only time there should ever be a problem is in the case of a bonefide cyclic dependency, which should not exist. we do not have any cycles in the current headers that I know if, so the only reason it should ever not work would likely be to a recent screw-up by someone.
@Peter Pronai note that this way of working over a git bridge is very sub-optimal and is now putting your entire project at risk because it's not committed to the main repository. please prioritize getting your patches separated and submitted cleanly. we'd rather you commit directly to the repository, not be working off on your own like this indefinitely. commit access requires at least a couple patches with no errors, conforms with style, makes some improvement (however minor), and that is mostly to demonstrate your competency in communicating change via patches that do not come with a burden.
you've clearly been very productive, but you got to work with @starseeker to get changes committed to the main svn repo. as I said on the prcolor patch months ago: "You're welcome to set up whatever you like, but submitting a 'complete' (tested and working) patch file is a fundamental developer skill that is independent of any revision control system or workflow. I think there is simply a mismatch in what you're trying to do and what is useful to share."
well,
cat Default/Default
no longer works
This is an example of something that would make for a perfect simple succinct patch that you should submit. Just the change that fixed it.
@Peter Pronai any luck?
@starseeker not really, i'm still trying to figure out how to group commits into patches :/
@starseeker not really, i'm still trying to figure out how to group commits into patches :/
still?? you can always pull a single commit and submit that or cherry pick a set of commits to a branch and then diff that ... or check out an svn repo and apply your commits one at a time (git diff SHA > git.diff1 ; git diff SHA > git.diff2 ; patch -p1 < git.diff1 ; patch -p1 < git.diff2 ; svn diff > mychanges.diff) .... or even just retype a succinct change on a fresh checkout.
@Peter Pronai at a minimum, you need to be talking through what you're doing and trying here so everyone knows what's going on ...
dead silence, struggling through it on your own without talking is the worst thing you can do
i've been looking for ways to group them more or less logically but i think i should just stop overthinking it and go with whatever comes to mind first
rebasing seemed like a good way to make the commits more coherent but i ran into some errors and i have no idea how to deal with them
i've been trying to automate this more or less or at least come up with ways to make it faster, but it looks like i'll just have to do everything manually
i made a copy of the master branch (which is the same as the svn repo) and i'm making patches
also, god bless CoW file systems, i didn't have to sacrifice a bunch of extra space for cloning the repo
alternatively, i thought about just separating the combined patch by file, but that still makes for quite a large patch in most cases
well, not just though about, i tried that a lot
but it didn't feel optimal and i hoped rebasing would be easier
hmmmmm.... i thiiiiiiiiiiiink i just rebased it successfully?
this just removed unrelated and reverted commits
i think i might be able to use rebase after all.....
better make a new branch though... just to be safe...
....i should be writing these to my log instead of rambling on the chat, shouldn't i
hmm, it didn't work out. let's cherry pick then...
....i should be writing these to my log instead of rambling on the chat, shouldn't i
rambling is perfectly fine, appreciated even ... better than only writing to your log :)
so, i'm going forward with the rebasing after all and splitting commits as needed, this should make them more manageable
after that, i can hopefully pick the changes out of order and present some nice patches
but the more realistic version is that there will be more patches that i'd like, but at least they will not include weird things
aaannnnd that's the rebase plan thingy done, i probably shouldn't run the rebase itself at 2:32 because that's just gonna lead to more problems, so i'll make a backup of the git-rebase-todo file and go dream of nicer commit histories
after that, i can hopefully pick the changes out of order and present some nice patches
@Peter Pronai to be honest, this sounds entirely unacceptable. It's been an entire week and you still have no patches. You could have retyped everything on an svn checkout in a day. You've used a full week, 10% of GSoC coding period timeline, learning git and commit/patch management. Please stop.
at some point, you just have to throw in the towel doing things the automatic way, and do things manually ... I think we're way way way past that point
I suggest that unless you have all your changes ready to go today, that you stop with git and simply make one feature change on an svn checkout, make an svn patch (svn diff > change1.patch), submit that, and then get another checkout for change#2, make the changes, make the patch, submit, etc.
you can extract a patchfile for every commit you've made with: git format-patch my_branch -o my_patches
you can get an svn checkout with: svn co svn+ssh://SFUSERNAME@svn.code.sf.net/p/brlcad/code/brlcad/trunk brlcad.trunk
if you don't have one already
soooo, I rebased it after all (sorry >_<) and now I have the patches
buuut there are 45 so I probably should send them each separately on SF
(there were more so, i think it's good i did those squashes)
guess i'll upload it here first
@Peter Pronai This isn't quite what we were looking for - rather than the various commits in git, we were looking for "feature" patches that provided compact, isolated improvements in capabilities.
@Peter Pronai I'm going to take this patch set and get these changes committed, so we can move forward - watch the commits. I'll post here once I've completed merging them.
@Peter Pronai OK - take a look at SVN commits r71280 thru r71286.
I saw a couple of things running that first example in the man page (or rather, the tweaked version from r71286 that produces a smaller output list with the m35.g example):
1. The exec version seems to be printing carriage returns, which it probably shouldn't if it's not printing the output explicitly in addition to the -exec (not sure what find does, but I would have expected it to either exec and not print or exec and print the search result list, not do the blank lines...)
2. When doing the full path version of the search -exec (e.g. supplying "/all.g" rather than "all.g") the draw command doesn't seem to be operating on the full paths - the "who" command, when run after the search -exec with "/all.g" should indicate it drew the full paths (this can matter if there are matricies along the full path.)
So, to be a little more explicit/specific:
open up share/db/m35.g
run the following search (note the slash before all):
mged> search /all.g -type region -and ( -below -bool - -or -below -bool + )
You should see:
/all.g/component/bed/r908/r914/r906/r916
/all.g/component/bed/r908/r914/r906
/all.g/component/bed/r914/r906/r916
/all.g/component/frame/r532/r506/r567
If you then clear your drawing list and do the draw search -exec:
mged> Z
mged> search /all.g -type region -and ( -below -bool - -or -below -bool + ) -exec draw "{}" ";"
I would expect to see either nothing or the same output as above (probably the former, unless I explicitly -print as well as -exec) but instead I see:
expected boolean value but got ""
The good news is does draw the objects, but if I run the "who" command - which lists what is being drawn:
mged> who
r567 r906 r916
That list show match the full path list above, since we did a full path search - if you explicitly draw the full paths and then do a who, you'll see draw can list them properly:
mged> Z
mged> draw /all.g/component/bed/r908/r914/r906/r916 /all.g/component/bed/r908/r914/r906 /all.g/component/bed/r914/r906/r916 /all.g/component/frame/r532/r506/r567
mged> who
/all.g/component/bed/r908/r914/r906/r916 /all.g/component/bed/r908/r914/r906 /all.g/component/bed/r914/r906/r916 /all.g/component/frame/r532/r506/r567
So it looks like somewhere in the exec pipeline we need to propagate awareness of which type of search we did (full path vs unique name) and adjust what is fed into -exec accordingly
@Peter Pronai very cool to see this working!
@Peter Pronai So from the current state, I suggest you try to make a focused patch that has -exec work on either the unique object list or the full path list depending on the search type. For -exec on full paths you'll probably want to use db_path_to_string to take the full path list and generate strings suitable for argv command execution.
Also, as a separate patch, I would see if you can eliminate the 'expected boolean value but got ""' error message
@starseeker in later versions of my branch i already use db_path_to_string, so that should be easy, but i'm not sure if the output actually should match the input at all times
uhm that's not the best way to put it...
so. BSD find(1) works on files, right?
ignoring bind mount shenanigans, there is only one path to a file and so that path uniquely identifies the file
but BRL-CAD is not like that, apparently.
an object is uniquely identified by its name and names are actually in a flat namespace. objects don't contain other objects, not really, they kind of just use them
at least that's my understanding so far
oh...
wait, nevermind.
the problem here is that paths aren't identifiers of an object
they identify a specific "use" of the object.... hm.
anyways, re: the noisy output
find(1) works with commands. commands have exit codes. Tcl commands don't have exit codes.
Tcl doesn't even have bools.
so, I turned to the only thing it has: text output.
-exec is supposed to be a filter, if Tcl had types, -exec would take a function that returns bools. but! printing is also the side-effect of some commands! now, how should -exec know if you are only running something for its side-effects?
the easy answer for me was that it simply shouldn't, the user is expected to work their Tcl magic to give search -exec
the output it expects
so, I think the nicer way to solve the noisy output problem would be to just let the user indicate that "hey, i'm running this purely for the side-effects, you can shut up about the format" and give them an equivalent to &>/dev/null
@Peter Pronai So maybe the more straightforward problem at first is to get exec working on the full path results, when search is in full path mode
@Peter Pronai could we at least have exec not print empty lines if the Tcl command doesn't produce any non-empty textual output? Or is that completely out of the control of exec?
that's controlled by mged's callback, so that's perfectly doable
hmmm...... from a Tcl scripting perspective, is it bad if we swallow empty lines? i think that could be misleading for scripts...
the current output isn't necessarily pretty, but it doesn't cause any surprises. I think that's better than being pretty??
idk, i've written some code to filter newlines out but i'm not sure how this affects scripting since I'm not familiar enough with Tcl idioms
We can try some tests, but I'd say first let's get exec operating on full path results when the search is a full path search
ok, but I think it'd be better if it always operated on full paths because that's more uniform
hmm.... inputs can be mixed, can't they?
....my spider sense is telling me this will get messy
ah well
this is a small fix i made yesterday: https://sourceforge.net/p/brlcad/patches/502/
find(1) works with commands. commands have exit codes. Tcl commands don't have exit codes.
Tcl commands have return codes -- they default to "TCL_OK" (aka "1") if a proc doesn't specify it or specifies "return"
hmmm...... from a Tcl scripting perspective, is it bad if we swallow empty lines? i think that could be misleading for scripts...
I would question who/what is generating empty lines in the first place, what utility that has and whether it could change. Gut reaction is that 'search' (and by extension search -exec) shouldn't be adding/removing/filtering lines. It should only print what it's been told to print (e.g., -print) or the output from commands it ran.
Tcl commands have return codes -- they default to "TCL_OK" (aka "1") if a proc doesn't specify it or specifies "return"
I got the impression that that was just for Tcl's internal error reporting, since none of my searches on how to do bools in Tcl said "well just use the return code like in bash", instead they all talked about string tests. So does that mean I can just use Tcl_Eval's return and skip the boolean conversion?
also I imagine it being used more like a filter function? since this is embedded scripting I can't help but apply my Lua mindset to it.
so, one-liners with anonymous functions and stuff
and it didn't seem like it had a way of returning a bool from an expression
ok, but I think it'd be better if it always operated on full paths because that's more uniform
an unpathed "object" is (typically) synonymous with "/object" ... just that many commands take only one or the other depending on the command's purpose
hmm, is there any command that behaves differently when given a path? i can't remember any off the bat...
I got the impression that that was just for Tcl's internal error reporting, since none of my searches on how to do bools in Tcl said "well just use the return code like in bash", instead they all talked about string tests. So does that mean I can just use Tcl_Eval's return and skip the boolean conversion?
Return codes are somewhat an orthogonal topic to bools. In Tcl, "everything is a string" -- https://wiki.tcl.tk/3018
Let's not get too distracted from the practical reality -- you're really referring to Tcl's C API (which is conceptually quite different from Tcl's Tcl API). For that, "man Tcl_Eval" and you'll see that you can indeed rely on a getting a whole host of potential return codes, each with a particular meaning.
and it didn't seem like it had a way of returning a bool from an expression
again, it matters if we're talking about Tcl or C here -- but you can return a value from either (on the Tcl side, see "man n return" and "man n catch")
it seems like return codes in Tcl are the poor-man's-exceptions....
oh I remember why i thought bool strings were the offical way, because boolean expressions "return" a 0 or 1 string
like expr 1 > 0
despite the supposed simplicity it is a surprisingly weird language
if {1 > 0} {echo a}
works but of course 1 > 0
doesn't mean anything on its own....
so, Tcl has integer returns but it doesn't use them like shells and there is no $?
or $status
, just catch
....
ah Tcl. fun thing I found out: search -exec echo '{}' ";"
(yes, possibly wrong quoting) in m35.g
basically freezes MGED
i mean, it proooobably finishes after a while, but unlike find(1)
which streams output nicely, Tcl has to collect everything in the result string (or object, but whatever), so it can't stream output.
not an earth-shattering revelation, yeah, but a nice example of why constructs like UNIX pipes are useful
it seems like return codes in Tcl are the poor-man's-exceptions....
Not really, just a completely different approach with tradeoff considerations. If all languages behaved the same, there wouldn't be much point to having different ones. :)
In the case of Tcl, their method fully separates the notion of return codes from output at a scripting level. That's particularly novel in comparison with posix/bash/ksh scripting where the two concepts are fully intermingled and you have to deal with it in convoluted logic or with presumptive convention. Even compared with a compiled language, Tcl's 'catch' command goes beyond catching a return code in that it can catch any error from a hierarchy of subcommands. Even C++-style exceptions can't really do that, or at least it would require some convention like using try-catch in leu of return codes -- which is typically quite a bad idea, discouraged.
if {1 > 0} {echo a}
works but of course1 > 0
doesn't mean anything on its own....
the rabbit hole goes MUCH deeper. in Tcl, everything is a command or a string. there is actually no if/then/else language construct like you're used to seeing. that 'if' statement there is actually running an 'if' _command_ that takes two strings... two arbitrarily complex strings
not an earth-shattering revelation, yeah, but a nice example of why constructs like UNIX pipes are useful
patches welcome! you could certainly make it stream the output back over a pipe :)
@Peter Pronai Can't you just feed the output from both types of returns to the commands?
My original thought was that you would take the two different types of results, get the string forms (either dp->d_namep for objects or db_path_to_string for full paths) and pass that on to the exec cmd. Does that not work?
@starseeker yeah, i've written something like that, it just has to communicate which one to use
but i've run into some weirdness with the Tcl result thing (it always returns an empty string for some reason?) and i'm gonna debug that first
@Sean yeah, i get if
being a command, but it feels a bit.... too special cased that it accepts expressions. it's certainly simpler than sh
but i'm not sure it is simpler/better than rc
but then again, rc is not an embedded language, so they are quite different beasts
it's an odd language for me because it looks like rc but doesn't have its main strengths (pipes) but was built to fit a niche similar to Lua but it doesn't have the strengths of that either
(so no metatables, no iterators, not fast like Lua, doesn't have closures, probably no TCO, etc.)
(it always returns an empty string for some reason?)
ah. i was a fool and was getting the result before the eval. :face_palm:
https://core.tcl.tk/tcllib/doc/trunk/embedded/www/tcllib/files/modules/lambda/lambda.html could this be used in MGED?
tbh what i'm more concerned about is that only the last error is printed because the result string is overwritten after every Tcl_Eval
@Sean yeah, i get
if
being a command, but it feels a bit.... too special cased that it accepts expressions. it's certainly simpler thansh
but i'm not sure it is simpler/better thanrc
I don't actually agree that it's simpler than shell other than from a language theory perspective (the conceptual model). the language expressiveness is far more complicated than shell, the capability is far more expressive too
definitely an odd language, but no more so than perl or shell in my mind, just different tradeoffs/benefits/warts
I'd argue Tcl is far more capable and expressive as a embedded command interpreter with natural syntax (e.g., "do this" not "do(&this)" or "this.do()" or what have you)
(over the likes of lua and shell at least)
I digress .. :) the result string getting smashed by subsequent calls happens in libged too, but how is that an issue for -exec? you're invoking each one so couldn't it capture the result? it's supposed to be returned as a true/false predicate for subsequent search command plan statements
example:
agua:~ morrison$ find test.cpp -exec true \; -exec echo true \; -exec false \; -exec echo false \;
true
it ran all three exec's except the last one because of the 'false' command's return code
@Peter Pronai how are things going?
@starseeker i made a small fix so far and have been trying to figure out how to get results out of -exec
nicely and how to pass callbacks nicely, but it's. eh. it's like... i get that the output is ugly but i don't know how to make it better???
i wanted to write more stuff for that fix but i couldn't figure out how to improve it
so i'll just send it over as it is
i gotta do some bureaucracy stuff for next semester so i'll be running around the city today
the paths vs names thing is still...... well idk if it's a good idea to do so much conversion based on the input
if the user wants to, they can easily transform a path into a name
soooo, either they should always get full paths or they should get to choose between two -exec flags or there should be two kinds of "holes"
these are also easier to implement than trying to pass down info about what the input was into c_exec
also, idk about how Tcl is usually used but could MGED have tools for higher-order functions and lambdas?
Tcl seems to have packages for that stuff but a quick test showed that the lambda package isn't in MGED
if the user wants to, they can easily transform a path into a name
they could, but this totally violates expectations and desired behavior. if I specify "search . -exec echo {}" vs "search / -exec echo {}", they imply a different result.
these are also easier to implement than trying to pass down info about what the input was into c_exec
maybe easier to implement but more complicated for users and error prone == not good
hmmm. well, what if I add the alternative hole syntax that forces filling with either the name or the full path but also modify the normal hole handling to use the input to decide how to fill it in?
the alternative hole syntax thingy is mostly done anyways
I don't understand what you mean by alternative hole
if you're pushing the work onto the user (i.e., making it more complicated and error-prone to use), then that is a complete non-starter
you mean some syntax other than {} ?
that would still be unfriendly, error-prone, unusual. can you recap what the problem is? getting basic substitution working doesn't seem like it should be that complicated to me -- the user already specified whether they want absolute or relative paths or a flat unique 'basename' listing
you just need to get that information to the code doing the exec or substitution
@Peter Pronai I've applied your 502 and 503 patches. Aside from the minor whitespace bit, they applied cleanly
@Peter Pronai I would really recommend trying the following, if you haven't done so already:
In f_exec, instead of directly pulling the dp pointer from db_node->path, check dp_node->path->fp_len
If fp_len > 1, use db_path_to_string to make a path to feed to the subsequent logic. else, use the object d_namep.
Also, what's the bu_fnmatch on search.c:1263 trying to achieve? We don't seem to be doing anything with that result, and it's not immediately clear to me what you're trying to match the name against?
Also, what's the bu_fnmatch on search.c:1263 trying to achieve? We don't seem to be doing anything with that result, and it's not immediately clear to me what you're trying to match the name against?
tbqh i had no idea at the time how to use the db_node as a short name so i just copy-pasted code from some simpler filter. later i rolled that change back and went back to using full path strings everywhere, but either i rolled that back too or Sean only applied some of the patches and they still included that incomplete code
tldr: i had no idea at the time what the thing i copied did so i left everything intact until i could see that it works at which point i would have eliminated unnecessary parts
https://sourceforge.net/p/brlcad/patches/504/
Any thoughts on the fullpath handling? We're getting down to the wire...
I'm not following why we can't just substitute full paths into the {} hole
@Peter Pronai it may be that some of the commands can't handle full paths properly, but that is the command's problem not that of search -exec
@Peter Pronai Please try the approach I outlined above and see what happens - even if it has problems, that patch should be a step in the right direction.
(Reminder)
In f_exec, instead of directly pulling the dp pointer from db_node->path, check dp_node->path->fp_len
If fp_len > 1, use db_path_to_string to make a path to feed to the subsequent logic. else, use the object d_namep.
https://sourceforge.net/p/brlcad/patches/507/ i guess i forgot to send this patch
there are a few other ones on SF
Got most of them applied - trying to test #507, but hitting some other problem that doesn't seem to be related to that patch
Alright, backed up a few commits and the patch works, so applying.
Ah, I missed this in r71461. You actually don't want to free the strings, since this level of memory management is not the level controlling the string pointers. Looks like we also want to make sure the splan entries are allocated before freeing. Fixed in r71464
As of that commit, search / -type shape -exec draw "{}" ";" works!
@Peter Pronai per your log, Tcl can do lambdas but I don't think you have enough Tcl experience or time to learn the nuances of using them. Moreover, I really can't envision what you possibly need them for that wouldn't be better or more simply implemented a different way. rather, the Tcl side should frankly be as lean as possible. this gets back to one of the earliest discussions I recall @starseeker and I having with you when the project got started, about whether to provide a hook back to Tcl or bind into libged. we still may need to go the latter route just from a consistency perspective (e.g., consider python scripts running search -exec in an embedded ged context), but there is undeniable appeal in being able to call arbitrary Tcl procs during exec. Regardless, lambdas are syntactic sugar -- if you have something begging for it, I'd like to see the specific case after evals are due. Before then, seems like a complete distraction/crutch/hammer/excuse.
Major kudos on the latest patch, @Peter Pronai! Core functionality is looking nearly ready. How are docs looking? What’s next?
I was doing bugfixes but ye, good reminder, I should update the docs regarding the specific format of paths.
I wanted to use lambdas because that's kinda what I'm used to, eval with string substitution is way uglier than passing in an anonymous function
eg. one of the examples I rewrote to use -exec instead of storing everything in an array does work but tbqh i was not sure how or why
basically, lambdas are nice for one-liners where you wanna do all sorts of fun things with the parameters
@Peter Pronai I think r71475 might address the confusion we both had about returning the results - I had the mistaken impression the array of fullpath results was trimmed down to a single db (which is not the case, and meant that once I tried a more hierarchy rich model I saw the problem.) Fortunately, we do have the necessary information available in db_node->flags.
So it's the same solution, just keying off of the flag rather than the path length
@starseeker isn't it different for single element paths?
I was getting some odd failures to print output - can you give me your opinion on commit r71476?
its the same logic you had in your #507 patch, but rather than checking the db_full_path length (which at this stage of the pipeline still has the full paths of all results, which was a conceptual mistake I made earlier) we can key off of the db_node->flags DB_SEARCH_RETURN_UNIQ_DP setting.
hmm
r71476 looks cleaner actually.... but afaik the result in Tcl is used for error handling not boolean returns?
but yeah it's probably better to always print the result...
I was getting problems with the search / -exec echo "{}" ";" output, and ended up tweaking how the returns are handled. What I'm not sure of is if I broke something else in doing so - I get the behavior I expect, but I'd like you to test it out and see if it's not doing what you expect.
$ echo 'search / -exec echo "{}" ";" ' | ./bin/mged ../../db/NIST_MBE_PMI_7-10.g /NIST_MBE_PMI_7-10.3dm /NIST_MBE_PMI_7-10.3dm/Default /NIST_MBE_PMI_7-10.3dm/Default/Solid1_4 /NIST_MBE_PMI_7-10.3dm/Default/Solid1_4/Solid1_4.s /NIST_MBE_PMI_7-10.3dm/Default/Solid1_3 /NIST_MBE_PMI_7-10.3dm/Default/Solid1_3/Solid1_3.s /NIST_MBE_PMI_7-10.3dm/Default/Solid1_2 /NIST_MBE_PMI_7-10.3dm/Default/Solid1_2/Solid1_2.s /NIST_MBE_PMI_7-10.3dm/Default/Solid1 /NIST_MBE_PMI_7-10.3dm/Default/Solid1/Solid1.s
that looks like the correct output
so my next question is how things behave with multiple -exec statements
for example, what's the behavior if you have one exec that does a mv {} {}.1 and then trys to exec a draw {} - will it do left-to-right ordering on execution? e.g. will the draws fail because {} is now at {}.1 thanks to the mv?
I'd suggest determining what the behavior is and documenting it in the search man page so users know what to expect.
$ echo 'search / -exec echo "{}" ";" -exec echo foo "{}" bar ";"' | ./bin/mged ../../db/NIST_MBE_PMI_7-10.g/NIST_MBE_PMI_7-10.3dm /NIST_MBE_PMI_7-10.3dm/Default /NIST_MBE_PMI_7-10.3dm/Default/Solid1_4 /NIST_MBE_PMI_7-10.3dm/Default/Solid1_4/Solid1_4.s /NIST_MBE_PMI_7-10.3dm/Default/Solid1_3 /NIST_MBE_PMI_7-10.3dm/Default/Solid1_3/Solid1_3.s /NIST_MBE_PMI_7-10.3dm/Default/Solid1_2 /NIST_MBE_PMI_7-10.3dm/Default/Solid1_2/Solid1_2.s /NIST_MBE_PMI_7-10.3dm/Default/Solid1 /NIST_MBE_PMI_7-10.3dm/Default/Solid1/Solid1.s
....huh...
hmm
soooo the question is "is this a lazy evaling and"
do multiple echos print out the output twice?
if not, figuring out why is the next step
mged> proc foo {x} { ? echo foo $x ? echo bar $x ? } mged> foo a bar a mged>
......................Tcl is confusing
oh wait nvm
wait. yes mind
i thought i misread it but no, the foo a
is the function call i entered
and that's not even a search -exec thing, that's hello world 2.0
why isn't it printing foo a
search / -exec echo "{}" ";" -exec echo "{}" ";"
What does that do? For that matter, what does find do with that on the OS command line?
mged> search / -exec echo "{}" ";" -exec echo "{}" ";" /NIST_MBE_PMI_7-10.3dm /NIST_MBE_PMI_7-10.3dm/Default /NIST_MBE_PMI_7-10.3dm/Default/Solid1_4 /NIST_MBE_PMI_7-10.3dm/Default/Solid1_4/Solid1_4.s /NIST_MBE_PMI_7-10.3dm/Default/Solid1_3 /NIST_MBE_PMI_7-10.3dm/Default/Solid1_3/Solid1_3.s /NIST_MBE_PMI_7-10.3dm/Default/Solid1_2 /NIST_MBE_PMI_7-10.3dm/Default/Solid1_2/Solid1_2.s /NIST_MBE_PMI_7-10.3dm/Default/Solid1 /NIST_MBE_PMI_7-10.3dm/Default/Solid1/Solid1.s
@Peter Pronai So, should it be reporting the results twice? That would have been my first guess, but I'd also suggest checking what find does in a similar case.
find output of /tmp
. . ./mozilla_rain0 ./mozilla_rain0 ./tmp.RZEtmTN5Tu ./tmp.RZEtmTN5Tu ./tmp.RZEtmTN5Tu/acme ./tmp.RZEtmTN5Tu/acme ./tmp.RZEtmTN5Tu/plumb ./tmp.RZEtmTN5Tu/plumb ./tmp.EzCqEyABTc ./tmp.EzCqEyABTc ./tmp.EzCqEyABTc/acme ./tmp.EzCqEyABTc/acme ./tmp.EzCqEyABTc/plumb ./tmp.EzCqEyABTc/plumb ./Temp-57bdf871-5e70-47a5-8180-755dda4eb98b ./Temp-57bdf871-5e70-47a5-8180-755dda4eb98b ./qipc_sharedmemory_qtox45bdc9c0c458c8a25fc32de3a5f6d795f60b53f9 ./qipc_sharedmemory_qtox45bdc9c0c458c8a25fc32de3a5f6d795f60b53f9 ./qipc_systemsem_qtox45bdc9c0c458c8a25fc32de3a5f6d795f60b53f9 ./qipc_systemsem_qtox45bdc9c0c458c8a25fc32de3a5f6d795f60b53f9 ./.esd-1000 ./.esd-1000 ./.esd-1000/socket ./.esd-1000/socket ./keepassxc-rain.socket ./keepassxc-rain.socket ./keepassxc-rain.lock ./keepassxc-rain.lock ./ns.rain.:0 ./ns.rain.:0 ./ns.rain.:0/plumb ./ns.rain.:0/plumb ./.X0-lock ./.X0-lock ./systemd-private-755da5f6ec004e17a9c73eeadc3d9ee7-chronyd.service-grVvTU ./systemd-private-755da5f6ec004e17a9c73eeadc3d9ee7-chronyd.service-grVvTU ./.Test-unix ./.Test-unix ./.font-unix ./.font-unix ./.XIM-unix ./.XIM-unix ./.ICE-unix ./.ICE-unix ./.X11-unix ./.X11-unix ./.X11-unix/X0 ./.X11-unix/X0
@Peter Pronai I've got to sign off for the evening - go ahead and dig into that and see what you can find (remember to discuss what you are finding on your log)
i should sleep too, but i'll be back in a few hours
well, g'nity
eg. one of the examples I rewrote to use -exec instead of storing everything in an array does work but tbqh i was not sure how or why
which example was that?
basically, lambdas are nice for one-liners where you wanna do all sorts of fun things with the parameters
very familiar with lambdas. :) very old programming construct; quite common with functional programming languages like ML and Lisp, and more recently getting surged attention in many languages with C/C++11 getting them.
Imho, there's very few instances outside of functional languages where they do more than save you (literally) 2-4 lines of code. Not that I don't appreciate the brevity, but there are many languages where that's not the a good fit with the processing model. It's like how it's awkward to write procedural code in Java or functional code in C++ .. you can do it but it's usually just bad practice and unmaintainable to anyone other than the original author.
tcl actually embraces lambda's very strongly (heck it's -everything-is-a-command- perspective means even basic language constructs like "if [] {}" is essentially a higher-order function being given two anonymous functions with automatic scope import/export), but it's atypical in user code. there are simply easier and more readable alternatives given the procedural command environment.
$ echo 'search / -exec echo "{}" ";" ' | ./bin/mged ../../db/NIST_MBE_PMI_7-10.g
FYI, this should be equiv: $ ./bin/mged -c ../../db/NIST_MBE_PMI_7-10.g search / -exec echo "{}" ";"
......................Tcl is confusing
That is mged's fault, not Tcls. Remember that your running a highly modified embedded interpreter there. It's doing a lot of things automatically that Tcl doesn't usually do, and some of those things complicate... for example, here's what you wrote in various forms all the way up to the right form:
agua:~ morrison$ tclsh % proc foo {x} { echo foo $x echo bar $x } % foo a invalid command name "echo" % proc foo {x} { puts foo $x puts bar $x } % foo a can not find channel named "foo" % proc foo {x} { puts "foo $x" puts "bar $x" } % foo a foo a bar a
In your example, 'echo' is an mged command, not a tcl command, and either it is wiping out the result or mged is running both, but resetting/overwriting as it iterates though the evaluation and gets results back from tcl.
@Peter Pronai So, should it be reporting the results twice? That would have been my first guess, but I'd also suggest checking what find does in a similar case.
it should, but mged may be getting in the way ... either mged, search, or the command (echo here) is probably wiping out the result string after each exec. as it also happens for me with puts, that implies it's either mged or search wiping out the result. another test with
echo "search . -exec puts \"{}\" \";\" -exec puts \"{}\" \";\" ; puts hello" | bin/mged test.g
implies that search is wiping out the result as it prints the hello along with the puts'
@Peter Pronai Definitely you want to try and figure out what's happening in the search code then - we definitely want the multi-echo case to work.
@Peter Pronai one data point I can offer - a quick check in gdb working with the share/db/moss.g example and the following search input:
search / -type tor -exec echo "{}" ";" -exec echo "{}" ";"
indicates that c_exec does seem to be getting called twice, but f_exec is only being called once - e.g. it's allocating two searchplan objects that are N_EXEC types, but only getting to one of them for execution.
I wonder if one problem we might be having here is if the search plan execution is interpreting the f_exec output as a "non-match" filter and halting filter processing once the first -exec "filter" is complete.
If that's the case, one option would be to make sure the f_exec "filter" always returns a match.
It also raises a question of what to do with mixed -exec calls and filters
Using the same moss.g example:
mged> search / -type arb8 -name box.s -exec echo "{}" ";"
/all.g/box.r/box.s
mged> search / -type arb8 -exec echo "{}" ";" -name box.s
/all.g/box.r/box.s
/all.g/platform.r/platform.s
Should those return the same result? What does find do in such a case?
One possibility is to store the exec plans separately and only execute them after all the other filters have been processed, but that's not the way to go if the expected find behavior is to only apply the filters defined before each exec (i.e. if we tack another echo onto the last example above, would the first echo print both box.s and platform.s and the second print just box.s ?)
Something to check in find's behavior
@Peter Pronai one data point I can offer - a quick check in gdb working with the share/db/moss.g example and the following search input:
search / -type tor -exec echo "{}" ";" -exec echo "{}" ";"
indicates that c_exec does seem to be getting called twice, but f_exec is only being called once - e.g. it's allocating two searchplan objects that are N_EXEC types, but only getting to one of them for execution.
so it's lazy evaluating, nice :+1: (that's what i expected tbh)
wait...
Should those return the same result? What does find do in such a case?
@starseeker it prints the same thing, since "echo" always returns true (well, it should at least, there is probably some weird case like a broken pipe error where it could return false)
@Sean why doesn't the echo command allow 0 arguments?
uh.... i removed the result string initialization from the echo command but it still doesn't work???
annd the wrapper function doesn't seem to be doing any resetting.....
and i didn't forget to build this time
hmmm.
oh
@starseeker TCL_OK is not non-zero.... ie. it is zero
I mean, do two echos print things twice, or does it print only once?
Yeah - find will do both exec calls:
find . -name testfile.txt -exec echo {} \; -exec echo {} \;
./testfile.txt
./testfile.txt
So I would then expect
search / -type tor -exec echo "{}" ";" -exec echo "{}" ";"
to print
/all.g/tor.r/tor
/all.g/tor.r/tor
@Peter Pronai Is patch #510 still needed with the current code?
Ah, I see patch #512 has got it.
So with that, we have search exec echo printing output, plus the "normal" search reporting. That doesn't seem to match the find behavior:
find . -name testfile.txt
./testfile.txt
find . -name testfile.txt -exec echo {} > /dev/null \;find . -name testfile.txt -exec echo {} \;
./testfile.txt
It looks like -exec may be regarded as a "printing filter" by find, which results in disabling the default output.
nevermind, I see it - c_exec wasn't setting the db_search_isoutput flag. Trivial fix, committed.
@Peter Pronai make sure you're updating your development log...
@Peter Pronai are you aware of any other problematic -exec behaviors?
@starseeker not really, echo still doesn't work but that's not search's fault
but puts works
really? echo works for me, at least in GUI mode...
....huh.
The last quirk I'm seeing is a consequence of my use of bu_log in mged_db_search_callback - I can't capture the results of search -exec into a Tcl variable, the way I can with a normal search.
What is echo doing when you try to run it?
I think to avoid having to use bu_log, we'd need to instead somehow build up an accumulated buffer of the result strings and then return them all at once at the end of the search command run.
in MGED, bu_log is literally putting text lines in the output window, and not accumulating them to return as part of the Tcl command result.
a b a b a b a b a b a b a b a b a b a b a b a b a b double free or corruption (fasttop) 8982: signal: sys: abort (core dumped) echo: exit 1
hmmmmmmmmmmmmmmmmmmmmmmmmmm
that's.... not good.
O.o
what are you searching?
or running?
0x00007f713adf5014 in bu_free (ptr=0x55d3bd9ab250, str=0x7f713e93740e "e_argv") at /home/rain/Sync/gsoc/brlcad-code/src/libbu/malloc.c:200
just a basic echo thingy
echo 'search -exec echo a ";" -exec echo b ";"' | ./bin/mged ../../db/moss.g
/me ponders... should the results of search -exec be storable in a Tcl variable as a result of running search? we might be able to do it with our own bu_log hook in the search callback...
Ah! an exec with no holes.
hadn't tried that
hrm... doesn't crash here.
Are you building the latest trunk build?
yup
well, latest as of ~10 minutes ago
Oh, there it is - takes two echo commands. One works.
oh
my bad
hey, this isn't the new and improved node freeer thingy i wrote that fixed my old and horrible node freeer thingy...
did i mess up my master branch??
(which should be a clone of the svn repo)
I applied a patch, then had to change it in a subsequent patch...
I might have made a mistake, but the patch I applied looked like it was freeing too much...
let me know if I missed something
well for one you aren't freeing the strings in argv....
waaait...
I'm not sure they should be freed - it depends where they come from.
here: e_argv[i] = bu_strdupm(**argvp, "e_argv arg");
dp->d_namep pointers from db_lookup generally aren't freed in calling code.
the d_namep stuff is in f_exec
, those aren't supposed to outlive the function
db_search_free_plan
cleans up after c_exec
r71486 should fix the echo double crasher
Let me back up and use your original freeer - if I missed the bu_strdupm I probably introduced a memory leak
yus!
it works
r71487 is close to your earlier patch, but if I add in the bu_free(splan->p_un.ex._e_argv[i], "e_argv[i]"); loop I get memory corruption. Did I strip out the bu_strdupm by mistake?
No, it's still there... why do I get a crash if I free it when I free the plans??
aren't you supposed to be freeing p
in the loop?
and not splan
?
quite right
r71488 works for me, but if I uncomment the loop freeing the e_argv entries I get a SIGSEGV
did I mess up p_un.ex._e_argc somehow?
doesn't seem like it
well... echo works...
what would be a good test that does some testing with -exec?
ah HAH!
valgrind to the rescue
I think r71489 has it.
@Peter Pronai at this point, I would double check that all the man page examples work.
good idea
The only other thing I can see that is missing is the possibility to capture the output of the execs into a Tcl variable, and that would probably require some pretty fancy voodoo at the libged search.c level with bu_log hooks - db_search is not set up to return arbitrary string output (which is not only possible but likely with arbitrary exec calls) so it would have to be handled at the libged level.
I would also add a man page example illustrating the behavior of multiple -exec calls - what to expect in terms of behavior.
you can use the moss.g examples from this chat log as a starting point - examples where I was wondering what the behavior would be (and what the right behavior should be) could be used to guide a man page discussion of the behavior of multiple -exec calls.
can't the result storing be done in Tcl?
since it's so very dynamic and whatnot?
one example might be if you have one -exec call that is a mv and a subsequent list call that is expecting the original name:
search . -exec mv "{}" "{}".new ";" -exec l "{}" ";"
I would expect all of the "l" list commands to fail, since the mv will be done first. On the other hand, I would expect
search . -exec mv "{}" "{}".new ";" -exec l "{}".new ":" to work
(Those would be good to try.)
@Peter Pronai the complication with storing the results in Tcl is that we're Tcl_Evaling the commands from exec in the interpreter, so accumulating the results in Tcl gets tricky. There's probably a way to make it work, but I'm not exactly sure how to do it.
You can certainly try to figure it out, but before doing that I would add the man page discussion, and make sure you are ready to submit what you need to submit to Google for the wrap-up.
hmmm
{}a is expanded to NAMEa
well
in find(1)
but c_exec only considers "{}" when it's on its own completely....
Am I correct that supporting that would require us to explicitly store the spaces when you're splitting up the -exec input strings?
the splitting is done in Tcl
hmmm...
but you're building up the exec string by filling in the holes, right?
yep
so the hole recognition logic would have to do something a bit different
but an argument is either replaced by a path or is a constant
c_exec has to store the argument string instead of NULLing holes
and scan for {} in them
and f_exec fills those in
/me nods - sounds right
shouldn't be too hard...
sounds good - just make sure you meet all the submission deadlines!
/me nods
this is awesome stuff - we've been wanting an -exec option in search for literally years
^u^
ok i think it finally works. wow. TIL water is wet and doing string stuff in C is prone to errors
./bin/mged ../../db/moss.g 'search -exec echo "{}AAAA{}{}B" ";" -exec echo "foo{}" "bar{}" ";"'
all.gAAAAall.gall.gB fooall.g barall.g light.rAAAAlight.rlight.rB foolight.r barlight.r LIGHTAAAALIGHTLIGHTB fooLIGHT barLIGHT tor.rAAAAtor.rtor.rB footor.r bartor.r torAAAAtortorB footor bartor ellipse.rAAAAellipse.rellipse.rB fooellipse.r barellipse.r ellipse.sAAAAellipse.sellipse.sB fooellipse.s barellipse.s cone.rAAAAcone.rcone.rB foocone.r barcone.r cone.sAAAAcone.scone.sB foocone.s barcone.s box.rAAAAbox.rbox.rB foobox.r barbox.r box.sAAAAbox.sbox.sB foobox.s barbox.s platform.rAAAAplatform.rplatform.rB fooplatform.r barplatform.r platform.sAAAAplatform.splatform.sB fooplatform.s barplatform.s
@Peter Pronai that patch didn't apply cleanly to trunk - did I miss a precursor patch?
hmm.
oh, nah, i forgot to merge master
hmm...
I did have one commit making sure we had a callback before trying to exec it - that might complicate the merge
np, i just reverted it then merged then reverted the revert then resolved the conflict
oops, i wasn't logged in....
ok i posted a fixed one
@starseeker can you check it?
It applies, looks good.
Be sure to mention in the docs that the pattern needs to look like:
search . -exec echo "{}.suffix" ";"
rather than
search . -exec echo "{}".suffix ";"
(I tried the latter first...)
how come the latter doesn't work?
search . -exec echo "{}".new ";"
extra characters after close-quote
hm. so it's just how Tcl quoting rules roll
looks like it
Applied r71497
I'd say doc updates and additions are probably next up?
Given how cool this is, we want to make sure users can use it!
sent the docs patch, I think that's basically it? gonna upload it now and send the eval
@Peter Pronai looking at your README.md - what are the conflicts in some of the commits? Is this the list of patches showing the stages of work (i.e. changes and subsequent changes in other commits?)
@starseeker it's (almost) all of the commits by me, not all of them were sent as patches
(the commits for making the "presentable" branch aren't included)
hmmmmmmm
come to think of it, maybe i should describe which branches are actually interesting......
@Sean I have a Tcl question, how come I can't group things like this:
search -exec eval { attr set {} color [expr {int(rand()*225)+30}]/[expr {int(rand()*225)+30}]/[expr {int(rand()*225)+30}] attr set {} shader {glass {tr 0.7 ri 1}} } ";"
I can run the search with either of those, but not both at the same time
i also tested
eval { puts a puts b }
and that worked as expected, so, something weird is going on...
@Peter Pronai Looking at the man page, for the search examples of "Bad" and "Good" do you think you could make a version that has one "correct" example paired with one "incorrect" example per table row? Having multiple "good" examples on the same line gives the impression that you need two exec calls instead of one to fix the one on the first row (for example.)
sure, I gotta slep now but I'll do it tomorrow afternoon
Last updated: Nov 15 2024 at 00:49 UTC