Multiple if conditions in Movable Type - if-statement

I'm working on a blog project in Movable Type and I'm absolutely stuck trying to get multiple conditions in an if statement to work.
The end goal is something the equivalent of this:
<mt:SetVar name="foo" value="bar">
<mt:SetVar name="foo1" value="bar1">
<mt:If name="foo" eq="bar">
<mt:If name="foo1" eq="bar1">
<div>Foobar Hooray</div>
</mt:If>
</mt:If>
I have tried as many of the usual combinations as I could think of, but is there one that I am missing? Or is this just not possible in MT?
Even the documentation doesn't reference multiple conditions.
https://movabletype.org/documentation/appendices/tags/if.html
These are the attempts made (not in order of attempt) and all will show as true, even though the first condition is false:
<mt:If name="foo" eq="barf" && name="foo1" eq="bar1">
<mt:If (name="foo" eq="barf") && (name="foo1" eq="bar1")>
<mt:If name="foo" eq="barf" & name="foo1" eq="bar1">
<mt:If (name="foo" eq="barf") & (name="foo1" eq="bar1")>
<mt:If name="foo" eq="barf" AND name="foo1" eq="bar1">
<mt:If (name="foo" eq="barf") AND (name="foo1" eq="bar1")>
<mt:If name="foo" eq="barf" And name="foo1" eq="bar1">
<mt:If (name="foo" eq="barf") And (name="foo1" eq="bar1")>
<mt:If name="foo" eq="barf" + name="foo1" eq="bar1">
<mt:If (name="foo" eq="barf") + (name="foo1" eq="bar1")>

Your first example with nested conditionals is most commonly used in my experience, but there are a couple ways you can achieve multiple tests in a single condition block.
First, we set the variables as you did:
<mt:Var name="foo" value="bar">
<mt:Var name="foo1" value="bar1">
The simplest test might be in Perl using the test attribute:
<mt:If test="$foo eq 'bar' && $foo1 eq 'bar1'">true<mt:Else>false</mt:If>
<mt:If test="$foo eq 'bar1' && $foo1 eq 'bar'">true<mt:Else>false</mt:If>
Extra caution would be prudent when using Perl in templates, and doing so will also require the server to have installed the Perl module Safe.
That said, you could perhaps make that slightly easier to maintain if you have a large number of variables to test by relying on interpolation:
<mt:If test='"$foo$foo1" eq "barbar1"'>true<mt:Else>false</mt:If>
<mt:If test='"$foo$foo1" eq "bar1bar"'>true<mt:Else>false</mt:If>
Or you can do something similar with normal template tags by first combining the variables (or not setting foo and foo1 in the first place, depending where their values come from):
<mt:SetVarBlock name="meta1"><mt:Var name="foo"><mt:Var name="foo1"></mt:SetVarBlock>
<mt:If name="meta1" eq="barbar1">true<mt:Else>false</mt:If>
<mt:If name="meta1" eq="bar1bar">true<mt:Else>false</mt:If>
The output of those tests should be:
true
false
true
false
true
false
You should even be able to construct a SetVarBlock with each component variable on separate lines for readability with judicious use of modifiers like strip_linefeeds and trim.
It has been quite some time since I have written much Movable Type code, so there are probably other ways. MT (and Perl) is quite flexible, and usually There Is More Than One Way To Do It. Hope that gets you closer!

Related

Robot Framework parsing for Notepad++ Function List

I'm trying to create a Notepad++ function list for Robot Framework scripts using the class structure to encapsulate the 4 different sections in a robot file:
Settings
Variables
Test Cases
Keywords
Using the documentation and some experimentation I created a simple filter that will return the keywords and test cases based on the fact that they start at the beginning of the line. But the more complex class grouping I need some regex help with. It seems that the *** should help with clear marking.
This is what I have thusfar:
I have installed the User Defined Robot Syntax Highlighting and have added the following section to the %app%\notepad++\functionList.xml
<association userDefinedLangName="Robotframework" id="robot_function"/>
And then in the parser section:
<parser
id="robot_function"
displayName="Robot Section"
commentExpr="((#.*?$)|(^Documentation*\w.*?$)|(^Meta*\w.*?$))|(^Library*\w.*?$)">
<function
mainExpr="^(\w.*?$)"
displayMode="$functionName">
<functionName>
<nameExpr expr="^(\w.*?$)"/>
</functionName>
</function>
</parser>
So, the part I'm having trouble with and I'd appreciate some help is:
<classRange mainExpr="^(\*).*(?=\n\S|\Z)">
<className>
<nameExpr expr="^(\w.*?$)"/>
</className>
<function mainExpr="^(\w.*?$)">
<functionName>
<nameExpr expr="^(\w.*?$)"/>
</functionName>
</function>
</classRange>
Below is an example robot file
*** Variables ***
${variable} variable value
*** Settings ***
Documentation multi
... line
... documentation.
Metadata Version 0.1
Library LibraryName some variable
Library String
*** Test Cases ***
Test Case RF 01
Run Keyword ${TEST_NAME}
Test Case RF 02
Run Keyword ${TEST_NAME}
*** Keywords ***
Test Case RF ${tc}
Sleep 30ms
Test Keyword
Sleep 300ms
I'm sure that if I can make it work for one of the sections, for example test cases, then that will allow me to also apply it to the other sections. Predominantly I'm interested in the test cases and keywords.
With the settings below I am able to use Notepad++ Function List for Robot Framework keywords and test cases:
<association id="robot_syntax" userDefinedLangName="Robotframework" />
<association id="robot_syntax" ext=".robot" />
<parser
displayName="Robot Framework"
id ="robot_syntax"
commentExpr="(^(\h*)|(#.*?)|(\[\w.*?)|(Documentation*\w.*?)|(Library*\w.*?)|(Metadata*\w.*?)|(Resource*\w.*?)|(Test (Setup|Teardown|Template|Timeout)*\w.*?)||(Suite (Setup|Teardown)*\w.*?)|((Force|Default) Tags*\w.*?))$"
>
<function mainExpr="(?m-s:(?:^)[A-Za-z0-9].*$)"/>
</parser>

Add a line in XML using batch file before a tag [duplicate]

This question already has answers here:
Changing tag data in an XML file using windows batch file
(3 answers)
Closed 6 years ago.
I have an XML file of the type
....
<sometag>
<tag attr_name1="long filename1" />
<tag attr_name2="long filename2" />
....
</sometag>
....
Before </sometag> I want to add <tag attr_name="long filename" /> so far the code in my Batch Script for Windows is:
#echo off
setlocal enableDelayedExpansion
set "newValue=^<tag attr_name="long filename" /^>
set newline=^& echo.
type "File.xml"|replace "(</tag>)" "!newValue!$1" >dupFile.xml
move /y "dupFile.xml" "File.xml"
I get "Path not found - </tag>" and File.xml contains "No Files Replaced". I know its not best practice to edit XML from batch file but can't use third party executables unfortunately. Can someone please help with where I'm going wrong?
Also I've checked the thread about changing data between tags in XML using batch file, when I use the suggested code there and alter to my requirement i get the above error.
If </sometag> appears only once in your XML and if it won't screw up the hierarchy inserting your new <tag> node on the line before, you could just loop through your XML file with a for /F loop, checking whether each line contains </sometag>. When found, simply echo your new <tag> prior to echoing the matched line.
#echo off
setlocal
>"newfile.xml" (
for /f "usebackq delims=" %%I in ("xmlfile.xml") do (
set "line=%%I"
setlocal enabledelayedexpansion
if not "!line!"=="!line:/sometag=!" (
echo ^<tag attr_name="long filename" /^>
)
endlocal
echo(%%I
)
)
type "newfile.xml"
But really, assuming your XML is fully valid, the most elegant solution is to parse the XML as structured data, rather than as complicated text to hack and scrape. That way it doesn't matter whether your code is beautified, minified, uglified, or whatever. The parser still understands it, regardless of indentation and line breaks. You can do this with languages already built into Windows -- VBScript, JScript, PowerShell, C#, and the list goes on. Batch might be the only language with which you're familiar, but it isn't the only language available in a base install of Windows. Here's a quick and dirty batch + PowerShell hybrid script to demonstrate. (Save it with a .bat extension.)
<# : batch portion (within a multiline PowerShell comment)
#echo off & setlocal
set "infile=test.xml"
set "parentNode=sometag"
set "newTag=tag"
set "newAttr=attr_name"
set "attrVal=long filename"
set "outfile=new.xml"
powershell -noprofile -noninteractive "iex (${%~f0} | out-string)"
type "%outfile%"
goto :EOF
: end batch (multiline comment) / begin PowerShell hybrid code #>
[xml]$xml = gc $env:infile
$parent = $xml.documentElement.selectSingleNode("//$env:parentNode")
$new = $xml.createElement($env:newTag)
[void]$new.setAttribute($env:newAttr, $env:attrVal)
[void]$parent.appendChild($new)
$xml.save($env:outfile)

xbuild with [System.Text.RegularExpressions.Regex]::Match(string,string) parameters doesn't work properly (MSBuild is fine)

I have a target that reads a .proj file with ReadLinesFromFile and then try to match a version number (e.g. 1.0.23) from the contained lines like:
<Target Name="GetRevision">
<ReadLinesFromFile File="$(MyDir)GetStuff.Data.proj">
<Output TaskParameter="Lines" ItemName="GetStuffLines" />
</ReadLinesFromFile>
<PropertyGroup>
<In>#(GetStuffLines)</In>
<Out>$([System.Text.RegularExpressions.Regex]::Match($(In), "(\d+)\.(\d+)\.(\d+)"))</Out>
</PropertyGroup>
<Message Text="Revision number [$(Out)]" />
<CreateProperty Value="$(Out)">
<Output TaskParameter="Value" PropertyName="RevisionNumber" />
</CreateProperty>
</Target>
The result is always empty.. Even if I try to do a simple Match($(In), "somestring") its not working correctly in linux/xbuild. This does work on windows/msbuild
Any tricks/ideas? An alternative would be to get the property version out of the first .proj file, instead of reading all lines and matching the number with a regex, but I don't even know if that is possible.
I am running versions:
XBuild Engine Version 12.0
Mono, Version 4.2.1.0
EDIT:
I've been able to trace it further down into the parameters that go into Match(), there is something wrong with the variables evaluation. The function actually works with for example Match("foobar","bar") I will get bar
But weird things happen with other inputs, e.g. Match($(In), "Get") will match Get because it is actually matching against the string "#(GetStuffLines)"
When I do Match($(In), "#..") I will get a match of #(G
But then, when I do Match($(In), "#.*") I actually get the entire content of the input file GetStuff.Data.proj which indicates that the variable was correctly expanded somewhere and the matching matched the entire input string.
I needed to circumvent Match() because it seems to be bugged at this point.
The ugly solution I came up with was to use Exec and grep the pattern like:
<Exec Command="grep -o -P '[0-9]+[.][0-9]+[.][0-9]+' $(MyDir)GetStuff.Data.proj > extractedRevisionNumber.tmp" Condition="$(OSTYPE.Contains('linux'))"/>
<ReadLinesFromFile File="$(ComponentRootDir)extractedRevisionNumber.tmp" Condition="$(OSTYPE.Contains('linux'))">
<Output TaskParameter="Lines" ItemName="GetExtractedRevisionNumber" />
</ReadLinesFromFile>
I couldn't even use the properties ConsoleToMSBuild and ConsoleOutput (https://msdn.microsoft.com/en-us/library/ms124731%28v=VS.110%29.aspx) because xbuild didn't recognize those.. That's why I grep the pattern and save it into a temp file which can be read with ReadLinesFromFile into the ItemName="GetExtractedRevisionNumber" that I use later.

Configuring Notepad++ "Function List" for Perl

I'm trying to get the "Fucntion List" feature on notepad++ (v 6.7.5) working for Perl with classes (or packages, in perl parlance). Only regular subroutines outside of packages are supported by default.
Below is the XML snippet in question from the Function List config file (located on my windows machine at C:\Users\user\AppData\Roaming\Notepad++\functionList.xml ). I added the "classRange" node myself on top of the default "function" node.
EDIT: below is the corrected XML, thanks to user stribizhev
UPDATE: I've commented out the "normal" function section, because it was causing all my methods to appear twice in the function list.
<parser id="perl_function" displayName="Perl">
<classRange mainExpr="^package.*?(?=\npackage|\Z)">
<className>
<nameExpr expr="\s\K[^;]+"/>
</className>
<function mainExpr="^[\s]*(?<!#)[\s]*sub[\s]+[\w]+[\s]*\(?[^\)\(]*?\)?[\n\s]*\{" displayMode="$className->$functionName">
<functionName>
<funcNameExpr expr="(sub[\s]+)?\K[\w]+"/>
</functionName>
</function>
</classRange>
<!--
<function mainExpr="^[\s]*(?<!#)[\s]*sub[\s]+[\w]+[\s]*\(?[^\)\(]*?\)?[\n\s]*\{" displayMode="$className->$functionName">
<functionName>
<nameExpr expr="(sub[\s]+)?\K[\w]+"/>
</functionName>
</function>
-->
</parser>
The documentation for this is here.
I tried your XML in Notepad++ 6.8.1 and while it does work for Perl with 'packages', my plain scripts without packages fail to produce subs now. I uncommented the lines you commented out and it fixes that problem, but does exhibit the behavior you mentioned - doubled up subs within the 'packages'.
I found the following works nicely and even ignores subs in POD (which may be there as example usage) so they aren't added to the list:
<parser id="perl_function" displayName="Perl" commentExpr="(#.*?$|(__END__.*\Z))">
<classRange mainExpr="(?<=^package).*?(?=\npackage|\Z)">
<className>
<nameExpr expr="\s\K[^;]+"/>
</className>
<function mainExpr="^[\s]*(?<!#)[\s]*sub[\s]+[\w]+[\s]*\(?[^\)\(]*?\)?[\n\s]*\{">
<functionName>
<funcNameExpr expr="(sub[\s]+)?\K[\w]+"/>
</functionName>
</function>
</classRange>
<function mainExpr="^[\s]*(?<!#)[\s]*sub[\s]+[\w]+[\s]*\(?[^\)\(]*?\)?[\n\s]*\{">
<functionName>
<nameExpr expr="(?:sub[\s]+)?\K[\w]+"/>
</functionName>
</function>
</parser>
Most probably, you should use funcNameExpr instead of nameExpr:
Example:
<functionName>
<funcNameExpr expr="(sub[\s]+)?\K[\w]+"/>
</functionName>
funcNameExpr or uncomment did not work for me. What worked - commenting out the three regex lines with parentheses:
#\(
#[^()]*
#\)
As Perl does not consider parentheses after function name correct, I do not have such. Parentheses can be at each call of the function, but not where the function is.

Inconsistent ifeq and ifneq

I can't figure out why in the following the two different versions yield different results:
$(INCLUDE_DIR)/%: TRGT_PATH = \
$(INCLUDE_DIR)/$(patsubst $(word 1,$(subst /, ,$(dir $*)))/%,%,$*)
$(INCLUDE_DIR)/%: INCLUDEDS = $(TRGT_PATH)
$(INCLUDED_FILES) : $(INCLUDE_DIR)/%: %
ifeq ($(TRGT_PATH),$(findstring $(TRGT_PATH),$(INCLUDEDS)))
#echo [INC][WARN] File $(TRGT_PATH) already exists while copying from $*
else
#echo $(findstring $(TRGT_PATH),$(INCLUDEDS))
#echo [INC] $* $(TRGT_PATH)
#cp $* $(TRGT_PATH)
endif
Output:
[INC][WARN] File include/GeometricObject.h already exists while copying from engine/GeometricObject.h
[INC][WARN] File include/util.h already exists while copying from engine/util.h
[INC][WARN] File include/util.h already exists while copying from test/util.h
If I change the line ifeq ($(TRGT_PATH),$(findstring $(TRGT_PATH),$(INCLUDEDS))) to ifneq (,$(findstring $(TRGT_PATH),$(INCLUDEDS))) the output is:
include/GeometricObject.h
[INC] engine/GeometricObject.h include/GeometricObject.h
include/util.h
[INC] engine/util.h include/util.h
include/util.h
[INC] test/util.h include/util.h
As far as I know $(findstring t,l) returns the t if t is in l and else an empty string. But the output (if VAR is equals LIST) still is:
foo
bar
Can someone explain?
PS: I tested a more simple code and that worked fine...
If you'd provided a complete example, including the values of VAR and LIST, we'd be able to help. As it is, all I can say is "it works for me", so you must not be accurately reporting your environment in your question:
$ cat Makefile
VAR = v
LIST = v
all:
ifneq (,$(findstring $(VAR),$(LIST)))
#echo foo
else
#echo bar
endif
$ make
foo
ETA:
Aha. Well, your problem has absolutely nothing to do with findstring, so it's not surprising that your original question was not answerable.
The issue is that you're trying to use target-specific variables in a non-recipe context, and the documentation clearly states that they are not available outside of recipes.
The ifeq, etc. statements are like preprocessor statements: they are evaluated as the makefile is being parsed, not later when the recipes are being invoked. So the values of TRGT_PATH and INCLUDEDS when the ifeq/ifneq is invoked are the global values of those variables (which might be the empty string if they're not set otherwise) not the target-specific values.