Double Edged Sword

Taming the wild ...

WILDCARDS

This is an abbreviated discussion of wildcards as a necessary preamble to the “Double Edged Sword” topic. For a complete list of the wild cards available (c/w sample function calls) please review the entry for the AutoLISP function WCMATCH in the AutoLISP Reference (available at http://bit.do/wcmatch).

For conversational convenience here is a wild card subset:

Asterisk "*"         Match any character sequence.
Comma ","            Separate multiple patterns.
Pound "#"            Match any single numeric digit.
Reverse quote "`"    Escape wild cards.

OK THEN …

One of the great things about AutoCAD is the simple but powerful ability to use a wild card specification in response to many commands that prompt for names at the command line.

For example, it’s easy to thaw all layers via “*” (match any character sequence, including an empty one) in response to the list prompt:

e.g.    -Layer [enter] thaw [ enter] Enter name list of layer(s) to thaw: *

It’s also easy to specify multiple layer names by separating them with commas:

e.g.    Enter name list of layer(s) to thaw: pipe1,pipe2,pipe3

One can also combine explicit names with wild carding:

e.g.    Enter name list of layer(s) to thaw: pipe*

A potential problem with the last example is that it is rather broad and indiscriminate — it will thaw layers “pipe”, “pipe1”, “pipe2”, “pipe3” as well as layer “pipe_temp”. This may not have been our intention. Perhaps we just wish to thaw “pipe” layers followed by one or two numerical digits.

Another wild card may prove useful, the pound (“#”) sign — match any single numerical digit.

e.g.    Enter name list of layer(s) to thaw: pipe#,pipe##

This would thaw layers “pipe1”, “pipe2″,”pipe3” thru to “pipe99” but not layers “pipe” or “pipe_temp”.

DOUBLE EDGE SWORD

So what does all this have to do with a double edged sword?

A problem arises when wild cards are used in layer (and other table, object) names. I often see drawings that sport layer, block, tab names etc. with periods “.”, commas “,” or pound signs “#” (to name but a few). The authors of these names may not realize the potential problem they are creating for themselves or for any programmer unaware of this little gotcha.

WHAT’S THE PROBLEM?

Let’s say a drawing has a layer named “pipe#1”.

If one tries to thaw said layer at face value:

e.g.    Enter name list of layer(s) to thaw: pipe#1

AutoCAD will not thaw layer “pipe#1”. It will thaw these layers:

pipe01,pipe11,pipe21 thru pipe91

But not “pipe#1”, as it does not match the wild card pattern. Remember AutoCAD is interpreting the pound sign in the filtering as a wild card place holder, substituting numbers 0 thru 9.

IS THERE AN EASY SOLUTION?

Yes.

Use the reverse quote character (“`”) to “escape” or “override” the wild card effect interpretation so it is interpreted literally. Note if you can’t find the reverse quote character look to the top left side of most keyboards, often sharing real estate with the tilde (“~”) character.

e.g.    Enter name list of layer(s) to thaw: pipe`#1

Layer “pipe#1” now thaws successfully.

HOW DOES THIS IMPACT PROGRAMMING?

There are several ways this can impact programming and if one is unaware of the problem it’s somewhat insidious.

For a simple contrived example consider the following. Assume a typical task is to process all entities on the active layout tab.

First a simple function to process an individual entity, let’s call it process_entity. It takes one argument, an entity name. For this example it will simply print out a few of the entity’s properties — the object type, layer and tab name.

(defun process_entity ( ename )
    (   (lambda ( format v2s data )
            (princ
                (strcat
                    (format "Object: "   0 data)
                    (format "Layer:  "   8 data)
                    (format "Tab:    " 410 data) "\n"
                )
            )
        )
        ;;  format output
        (lambda ( name key data ) (strcat "\n" name "[" (v2s key data) "]"))
        ;;  get & convert select item
        (lambda ( key data ) (vl-prin1-to-string (cdr (assoc key data))))
        ;;  raw data   
        (entget ename)
    )
    (princ)
)

Now let’s write a wrapper function to iterate thru all entities on the
active layout tab:

(defun process_active_tab ( / ctab ss i )
    (if (setq ss (ssget "x" (list (cons 410 (setq ctab (getvar 'ctab))))))
        (repeat (setq i (sslength ss))
            (process_entity (ssname ss (setq i (1- i))))
        )
        (princ (strcat "\nNo entities found on tab [\"" ctab "\"]."))
    )
    (princ)
)

Assuming we are in a paper space tab named “Layout1” and said tab hosts some entities we might see something like the following when we execute it:

(process_active_tab)

Object: ["VIEWPORT"]
Layer:  ["vports"]
Tab:    ["Layout1"]

Object: ["LWPOLYLINE"]
Layer:  ["pipe#1"]
Tab:    ["Layout1"]

Object: ["TEXT"]
Layer:  ["text"]
Tab:    ["Layout1"]

Now rename the tab to “Layout#1” and re-execute the function.

(process_active_tab)

AutoCAD reports:

No entities found on tab ["Layout#1"].

No good. We know this layout tab hosts entities. How can we deal with this scenario and ensure that (regardless the naming convention) we properly process the active layout tab?

Simple. Write a function that takes a string as an argument. If the string contains wild cards return a new string with each wild card prefixed with a reverse quote, otherwise return the original string. This way it will work with “normal” and wild card hosting strings.

(defun tame ( text / wild )
    (if (wcmatch text (strcat "*[" (setq wild "-~#@.*?`,") "]*"))
        (   (lambda ( lst nfg / result )
                (vl-list->string
                    (foreach x (reverse lst)
                        (setq result
                            (if (member x nfg)
                                (vl-list* 96 x result)
                                (cons x result)
                            )
                        )
                    )
                )
            )
            (vl-string->list text)
            (vl-string->list wild)
        )
        text
    )
)

Test it:

(tame "layout#1")

AutoCAD returns:

"layout`#1"

Perfect, let’s implement it:

(defun process_active_tab ( / ctab ss i )
    (if (setq ss (ssget "x" (list (cons 410 (tame (setq ctab (getvar 'ctab)))))))
        (repeat (setq i (sslength ss))
            (process_entity (ssname ss (setq i (1- i))))
        )
        (princ (strcat "\nNo entities found on tab [\"" ctab "\"]."))
    )
    (princ)
)

Test it:

(process_active_tab)

AutoCAD reports:

Object: ["VIEWPORT"]
Layer:  ["vports"]
Tab:    ["Layout#1"]

Object: ["LWPOLYLINE"]
Layer:  ["pipe#1"]
Tab:    ["Layout#1"]

Object: ["TEXT"]
Layer:  ["text"]
Tab:    ["Layout#1"]

A COUPLE FINAL NOTES

AutoCAD sports a system variable to control how many, and what types of characters are allowed in table names (blocks, layers, dim styles etc).

The variable is named EXTNAMES. When set to 0 (zero) it takes an AutoCAD R14 stance, limiting names to 31 characters in length, and the letters A to Z, numbers 0 to 9, the dollar sign ($), the underscore (_) and hyphen (-).

Seemingly this would eliminate the problem floated above.

However, this restriction (curiously) does not apply to layout tab names. So regardless the EXTNAMES system variable setting the contrived challenge I presented can still exist.

Also, AutoLISP provides a function to check the validity of a symbol name, SNVALID. It works in concert with the value of EXTNAMES. In short, if EXTNAMES is 0, SNVALID will validate a name using the rules in effect in AutoCAD R14.

While one could write incorporating the above variable settings and functionality it’s far simpler to just write a simple function (ala TAME) to deal with any situation which may be impacted by the issue.

RELEVANT LINKS

http://bit.do/wcmatch Apply wild card pattern matching …
http://bit.do/extnames Control the characters accepted …
http://bit.do/snvalid Determine if the symbol name is valid …

RANDOM

Did you know the original (modal) layer dialog (DDLMODES for you AutoCAD vets) is still available? Simply type CLASSICLAYER at AutoCAD’s command line. Ditto for CLASSICXREF, CLASSICIMAGE etc. An aside, DDLMODES invokes the new, rather than the original layer dialog; funny that.

Are articles like this of any value?

Let me know, thanks!

Michael