What is the proper way to execute a batchfile with multiple params? - delphi

I have a batchfile which I use for managing the translation of various programs.
Now I want a delphi application to call this batchfile and pass on the parameter it needs for further processing. Unfortunately the parameters contain spaces which leads to a splitup. Is there a way to keep all parameters tied up as intended?
this is how my batchfile looks:
ECHO Scan for new ressources
%MLDIR%\Ml7Build.exe s %1%
ECHO Import glossary for new translation
%MLDIR%\MlBuild.exe i %2%
ECHO Create translated application
%MLDIR%\Ml7Build.exe b %3%
I tried to use the ShellExecute-Command from ShellApi because I found several similar questions on SO, but none of them could help me in solving my problem. My delphi code looks like this:
param1 := ExtractFileName(hMLProj);
param2 := '-f: '+MLWorkDir+'Prev_'+ExtractFileName(hMLProj)+' -settings:Auftrag_Test.importsettings-method:2 -overwri:3 -error:2 '+ExtractFileName(hMLProj)+' ';
param3 := ExtractFileName(hMLProj);
ShellExecute(0,'open',PCHAR(MLWorkDir+'__AutomatedTranslationFUBAR.bat'),PChar(param1 +param2 +param3),nil,SW_SHOWDEFAULT);

ECHO Scan for new resources
%MLDIR%\Ml7Build.exe s %~1
ECHO Import glossary for new translation
%MLDIR%\MlBuild.exe i %~2
REM is 7 ^ omitted here?
ECHO Create translated application
%MLDIR%\Ml7Build.exe b %~3
Note that %n not %n% (n=1..9) refers to the parameter n supplied to the batch. The tilde removes "any enclosing quotes."
Parameters require to be "enclosed in quotes" (and they must be double-quotes) if they contain separators such as spaces.

Related

How to issue Message Before Build--or seq problems

I'm trying to add helpful messages for arbitrary builds. If the build fails the user can, for example, install the package with different arguments.
My interface idea is to provide a function, build-with-message, that would be called with something like this:
build-with-message
''Building ${pkg.name}. Alternative invocations are: ..''
pkg
My implementation is based on builtins.seq
build-with-message = msg : pkg :
seq
(self.runCommand "issue-message" {} ''mkdir $out; echo ${msg}'')
pkg;
When I build a package with build-with-message I never see the message. My hunch is that seq evaluates the runCommand far enough to see that a set is returned and moves on to building the package. I tried with deepSeq as well, but a deepSeq build fails on runCommand. I also tried calling out some attributes from the runCommand, e.g.
(self.runCommand "issue-message" {} ''mkdir $out; echo ${msg}'').drvPath
(self.runCommand "issue-message" {} ''mkdir $out; echo ${msg}'').out
My thought being that calling for one of these would prompt the rest of the build. Perhaps I'm not calling the right attribute, but in any case the ones I've tried don't work.
So:
Is there a way to force the runCommand to build in the above scenario?
Is there already some builtin that just lets me issue messages on top of arbitrary builds?
Here's me answering my own question again, consider this a warning.
Solution:
I've in-lined some numbered comments to help with the explanation.
build-with-message = msg : pkg :
let runMsg /*1*/ = self.runCommand "issue-message"
{ version = toString currentTime; /*2*/ } ''
cat <<EOF
${msg}
EOF
echo 0 > $out /*3*/
'';
in seq (import runMsg /*4*/) pkg; /*5*/
Explanation:
runMsg is the derivation that issues the message.
Adding a version based on the current time ensures that the build of runMsg will not be in /nix/store. Otherwise, each unique message will only be issued for the first build.
After the message is printed, a 0 is saved to file as the output of the derivation.
The import loads runMsg--a derivation, and therefore serialized as the path $out. Import expects a nix expression, which in this case is just the number 0 (a valid nix expression).
Now, since the runMsg output will not be available until after it has been built, the seq command will build it (issuing the message) and then build pkg.
Discussion:
I take note of Robert Hensing's comment to my question--this may not be something Nix was not intended for. I'm not arguing against that. Moving on.
Notice that issuing a message like so will add a file to your nix store for every message issued. I don't know if the message build will be garbage collected while pkg is still installed, so there's the possibility of polluting the nix store if such a pattern is overused.
I also think it's really interesting that the result of the runMsg build was to install a nix expression. I suppose this opens the door to doing useful things.

Enclosing a python Path variable in quotes?

I need help fixing a Python script, but know ABSOLUTELY NOTHING about Python (though I am an experienced programmer so I get the jargon.)
I have this script that is trying to write output to a generated path, but when I run it, it gives me an error saying something about "E:\Program", and when I check, sure enough, it creates a folder named "Program" in the root of my E: drive. I'm CERTAIN it is trying to write to "E:\Program Files" but the space is terminating the command.
I did find where "path" is assigned:
path = tkm.path_archuncomp + bs.path + bs.name + '.ext'
How would I enclose that path in quotes that are assigned to the variable?
Inserting the quotes character appears to be the same as languages like C. Simply use "slash" notion:
path = \" + some_variable + \"
Unfortunately for me, repairing the script is more complicated than that, and doing so only created errors, so I can't confirm that was done properly. :(

Error loading file with full name containing spaces in directory with delphi

I am using XE8, win 8.1.
When trying load a file with spaces in directory, I am getting a exception of syntax name of the file or directory is invalid.
If I use imageen dialog to preview the file, no erros are found.
I did two tests with the procedure load_file1 and load_file2 and I have the same problem.
Is there a wrokaround to solve it?
function get_file:string;
begin
result:='"C:\Compartilhada\dicomserver versoes\dicomserverx\data\Genesis-1000\1.2.410.200013.1.215.1.200912141600580009_0001_000001_13061821270002.dcm"'
end;
procedure load_file1;
var fStm:Tstream;
p1:string;
begin
p1:=get_file;
fStm := tFileStream.Create( p1, fmOpenRead or fmShareDenyNone ); //->Error Here
try
TBlobField(FieldByName('dicom')).LoadFromStream(fStm);
Post;
finally
fSTm.Free;
end;
end;
procedure load_file2;
p1:string;
begin
p1:=get_file;
TBlobField(FieldByName('dicom')).LoadFromFile(p1); //-->Error Here
Post;
end;
Remove the double quote marks from your string. It should be:
'C:\Compartilhada\dicomserver versoes\dicomserverx\data\Genesis-1000\1.2.410.200013.1.215.1.200912141600580009_0001_000001_13061821270002.dcm'
You might use " for paths containing spaces in some situations, for instance a command interpreter. But at the API level, it is simply not needed. And indeed it is a mistake as you have discovered. The double quote character " is actually a reserved character in a file name. That is documented on MSDN:
Naming Files, Paths, and Namespaces: Naming Conventions
The following fundamental rules enable applications to create and process valid names for files and directories, regardless of the file system:
...
Use any character in the current code page for a name, including Unicode characters and characters in the extended character set (128–255), except for the following:
The following reserved characters:
< (less than)
> (greater than)
: (colon)
" (double quote)
/ (forward slash)
\ (backslash)
| (vertical bar or pipe)
? (question mark)
* (asterisk)
...
...
In comments below you indicate that the code in the question does not reflect your actual problem. Which makes me wonder how you expect us to help. Your real problem is not the error message produced by the specific code, but that your debugging skills are letting you down. Let me try to explain how to debug a problem like this.
First of all, you are passing a file name to LoadFromFile or TFileStream.Create. These calls fail with an error that indicates that the file name is not valid.
So, when faced with that knowledge, the first step is to check the value of the file name that you are passing. Use debugging techniques to do that. Either the IDE debugger, or logging.
Once you have identified what value you are actually passing to these functions you can try to work out what is invalid about it.
To repeat, your real problem is not with the specifics, but in your debugging skills. You should take this as an opportunity to learn more about debugging. Stack Overflow is not a substitute for debugging. Learn to debug better, and your life as a programmer will become very much easier.

Validate URL in Informix 4GL program

In my Informix 4GL program, I have an input field where the user can insert a URL and the feed is later being sent over to the web via a script.
How can I validate the URL at the time of input, to ensure that it's a live link? Can I make a call and see if I get back any errors?
I4GL checking the URL
There is no built-in function to do that (URLs didn't exist when I4GL was invented, amongst other things).
If you can devise a C method to do that, you can arrange to call that method through the C interface. You'll write the method in native C, and then write an I4GL-callable C interface function using the normal rules. When you build the program with I4GL c-code, you'll link the extra C functions too. If you build the program with I4GL-RDS (p-code), you'll need to build a custom runner with the extra function(s) exposed. All of this is standard technique for I4GL.
In general terms, the C interface code you'll need will look vaguely like this:
#include <fglsys.h>
// Standard interface for I4GL-callable C functions
extern int i4gl_validate_url(int nargs);
// Using obsolescent interface functions
int i4gl_validate_url(int nargs)
{
if (nargs != 1)
fgl_fatal(__FILE__, __LINE__, -1318);
char url[4096];
popstring(url, sizeof(url));
int r = validate_url(url); // Your C function
retint(r);
return 1;
}
You can and should check the manuals but that code, using the 'old style' function names, should compile correctly. The code can be called in I4GL like this:
DEFINE url CHAR(256)
DEFINE rc INTEGER
LET url = "http://www.google.com/"
LET rc = i4gl_validate_url(url)
IF rc != 0 THEN
ERROR "Invalid URL"
ELSE
MESSAGE "URL is OK"
END IF
Or along those general lines. Exactly what values you return depends on your decisions about how to return a status from validate_url(). If need so be, you can return multiple values from the interface function (e.g. error number and text of error message). Etc. This is about the simplest possible design for calling some C code to validate a URL from within an I4GL program.
Modern C interface functions
The function names in the interface library were all changed in the mid-00's, though the old names still exist as macros. The old names were:
popstring(char *buffer, int buflen)
retint(int retval)
fgl_fatal(const char *file, int line, int errnum)
You can find the revised documentation at IBM Informix 4GL v7.50.xC3: Publication library in PDF in the 4GL Reference Manual, and you need Appendix C "Using C with IBM Informix 4GL".
The new names start ibm_lib4gl_:
ibm_libi4gl_popMInt()
ibm_libi4gl_popString()
As to the error reporting function, there is one — it exists — but I don't have access to documentation for it any more. It'll be in the fglsys.h header. It takes an error number as one argument; there's the file name and a line number as the other arguments. And it will, presumably, be ibm_lib4gl_… and there'll be probably be Fatal or perhaps fatal (or maybe Err or err) in the rest of the name.
I4GL running a script that checks the URL
Wouldn't it be easier to write a shell script to get the status code? That might work if I can return the status code or any existing results back to the program into a variable? Can I do that?
Quite possibly. If you want the contents of the URL as a string, though, you'll might end up wanting to call C. It is certainly worth thinking about whether calling a shell script from within I4GL is doable. If so, it will be a lot simpler (RUN "script", IIRC, where the literal string would probably be replaced by a built-up string containing the command and the URL). I believe there are file I/O functions in I4GL now, too, so if you can get the script to write a file (trivial), you can read the data from the file without needing custom C. For a long time, you needed custom C to do that.
I just need to validate the URL before storing it into the database. I was thinking about:
#!/bin/bash
read -p "URL to check: " url
if curl --output /dev/null --silent --head --fail "$url"; then
printf '%s\n' "$url exist"
else
printf '%s\n' "$url does not exist"
fi
but I just need the output instead of /dev/null to be into a variable. I believe the only option is to dump the output into a temp file and read from there.
Instead of having I4GL run the code to validate the URL, have I4GL run a script to validate the URL. Use the exit status of the script and dump the output of curl into /dev/null.
FUNCTION check_url(url)
DEFINE url VARCHAR(255)
DEFINE command_line VARCHAR(255)
DEFINE exit_status INTEGER
LET command_line = "check_url ", url
RUN command_line RETURNING exit_status
RETURN exit_status
END FUNCTION {check_url}
Your calling code can analyze exit_status to see whether it worked. A value of 0 indicates success; non-zero indicates a problem of some sort, which can be deemed 'URL does not work'.
Make sure the check_url script (a) exits with status zero on success and non-zero on any sort of failure, and (b) doesn't write anything to standard output (or standard error) by default. The writing to standard error or output will screw up screen layouts, etc, and you do not want that. (You can obviously have options to the script that enable standard output, or you can invoke the script with options to suppress standard output and standard error, or redirect the outputs to /dev/null; however, when used by the I4GL program, it should be silent.)
Your 'script' (check_url) could be as simple as:
#!/bin/bash
exec curl --output /dev/null --silent --head --fail "${1:-http://www.example.com/"
This passes the first argument to curl, or the non-existent example.com URL if no argument is given, and replaces itself with curl, which generates a zero/non-zero exit status as required. You might add 2>/dev/null to the end of the command line to ensure that error messages are not seen. (Note that it will be hell debugging this if anything goes wrong; make sure you've got provision for debugging.)
The exec is a minor optimization; you could omit it with almost no difference in result. (I could devise a scheme that would probably spot the difference; it involves signalling the curl process, though — kill -9 9999 or similar, where the 9999 is the PID of the curl process — and isn't of practical significance.)
Given that the script is just one line of code that invokes another program, it would be possible to embed all that in the I4GL program. However, having an external shell script (or Perl script, or …) has merits of flexibility; you can edit it to log attempts, for example, without changing the I4GL code at all. One more file to distribute, but better flexibility — keep a separate script, even though it could all be embedded in the I4GL.
As Jonathan said "URLs didn't exist when I4GL was invented, amongst other things". What you will find is that the products that have grown to superceed Informix-4gl such as FourJs Genero will cater for new technologies and other things invented after I4GL.
Using FourJs Genero, the code below will do what you are after using the Informix 4gl syntax you are familiar with
IMPORT com
MAIN
-- Should succeed and display 1
DISPLAY validate_url("http://www.google.com")
DISPLAY validate_url("http://www.4js.com/online_documentation/fjs-fgl-manual-html/index.html#c_fgl_nf.html") -- link to some of the features added to I4GL by Genero
-- Should fail and display 0
DISPLAY validate_url("http://www.google.com/testing")
DISPLAY validate_url("http://www.google2.com")
END MAIN
FUNCTION validate_url(url)
DEFINE url STRING
DEFINE req com.HttpRequest
DEFINE resp com.HttpResponse
-- Returns TRUE if http request to a URL returns 200
TRY
LET req = com.HttpRequest.create(url)
CALL req.doRequest()
LET resp = req.getResponse()
IF resp.getStatusCode() = 200 THEN
RETURN TRUE
END IF
-- May want to handle other HTTP status codes
CATCH
-- May want to capture case if not connected to internet etc
END TRY
RETURN FALSE
END FUNCTION

Can xgettext be used to extract specific domain strings only?

(Really surprised this isn't answered anywhere online; couple posts over the past few years with a similar question, but never answered. Let's hope the Stackoverflow crew can come to the rescue)
Situation:
When using gettext to support application localization, one sometimes wishes to specify a 'domain' with dgettext('domain', 'some text string'). However, when running xgettext, all strings wrapped with dgettext(...) are spit out into one file (default: messages.po).
Given the following example:
dgettext('menus', 'login link');
dgettext('menus', 'account link');
dgettext('footer', 'copyright notice');
dgettext('footer', 'contact form');
is there any way to end up with
menus.po
footer.po
using an extractor such as xgettext?
PHP response desired, although I believe this should be applicable across all languages
The only way I've found to do this is to redefine gettext functions...
example:
function _menus ($str) {
return dgettext('menus', $str);
}
function _footer ($_str) {
return dgettext('footer', $str);
}
_menus('login link');
_menus('account link');
_footer('copyright notice');
_footer('contact form');
else, you only have to run following commands:
xgettext [usual options] -k --keyword=_menus:1 -d menus
xgettext [usual options] -k --keyword=_footer:1 -d footer
Bye!
I do not know how to put different contexts in different files, but I did find that xgettext can extract the domain names into msgctxt fields in the po file. For PHP this is not done by default. To enable this, use for example --keyword=dgettext:1c,2 (in poedit, add "dgettext:1c,2") to the keyword list.
See also:
http://developer.gnome.org/glib/2.28/glib-I18N.html
https://www.gnu.org/savannah-checkouts/gnu/gettext/manual/html_node/xgettext-Invocation.html
Achieving this is best done through either code separation or the use of context disambiguation.
If you can separate your menu code from your footer code, then you can truly consider them different domains and extract them accordingly from known locations.
If modular separation is impossible and all the code lives together, then really you should be using context instead of domains. e.g.
translate( 'A string', 'myproject', 'some module' )
Where "myproject" is your domain and "some module" disambiguates the string.
However, reality doesn't always align with best practice, so if you can't refactor your code as Asevere suggests (and that is probably the best answer) then I have a massive hack to offer.
You could exploit the context flag mentioned in Boris's answer - We can repurpose this but only if we're not otherwise going to be using contexts.
I'll repeat that. This hack will only work if your code is not using contexts.
Some PHP holding two domains (including one string used in both) -
<?php // test.php
dgettext( 'abc', 'foo' );
dgettext( 'abc', 'bar' );
dgettext( 'xyz', 'bar' );
We can cheat, and take the domain argument as if we intended it to be the message context (msgctxt field). Extracting from the command line:
xgettext -LPHP --keyword=dgettext:1,2c -o - test.php \
| sed 's/CHARSET/utf-8/' \
> combined.pot
This generates a combined.pot file containing all the strings with our context hack. (note we also fixed the placeholder character set field which would break the next bit)
We can now filter out all messages of a given context into separate files using msggrep. Note we also trash the context field as we're not using it.
msggrep -J -e foo -o - combined.pot | sed '/^msgctxt/d' > foo.pot
msggrep -J -e bar -o - combined.pot | sed '/^msgctxt/d' > bar.pot
Improper, but it works.

Resources