NSIS "Bad text encoding" compilation error from _EndSwitch macro - nsis

We've been using NSIS 2.50 for some time now and I'm trying to update to the newest 3.0x version. I'm using Unicode true. I've ran into an issue, which I'm failing to understand. We use a switch for mapping native language names to language IDs, more or less like this:
${Switch} "${LANGNAME}"
${Case} "${LANGFILE_ALBANIAN_NAME}"
StrCpy $0 "${LANG_ALBANIAN}"
${Break}
${Case} "${LANGFILE_ARABIC_NAME}"
StrCpy $0 "${LANG_ARABIC}"
${Break}
; Other cases
${EndSwitch}
The error I'm getting from compilation:
Bad text encoding: C:\Users\me\AppData\Local\Temp\nst9352.tmp:9
!include: error in script: "C:\Users\me\AppData\Local\Temp\nst9352.tmp" on line 9
Error in macro _EndSwitch on macroline 9
The temporary file is apparently created by LogicLib, which then tries to include it. The file really doesn't have any valid Unicode encoding (I'm posting just a snippet from the file):
!insertmacro _== `$2` `Shqip` _LogicLib_Label_433 ""
!insertmacro _== `$2` `???????` _LogicLib_Label_434 ""
!insertmacro _== `$2` `Catal�` _LogicLib_Label_441 ""
The strings with invalid UTF-8 characters seem to be encoded in various ANSI encodings (some seem to be Western European, some Central European etc.), while the question marks are saved as real question marks (0x3F). ${LANGFILE_NLFID_NAME} is defined in language files as native name using the LANGFILE macro from LangFile.nsh. I looked at the language files and they are encoded in UTF-8 BOM and look all right. So it looks like the native name is re-encoded to ANSI or something for ${LANGFILE_NLFID_NAME}?
I'm pretty sure I'm making some stupid mistake, but I can't really figure out what it is.

Looks like a bug, it will be fixed in the next release.
Since you are not using the fall-through Switch feature you can just use Select or If/ElseIf instead:
${Select} "${LANGNAME}"
${Case} "${LANGFILE_ALBANIAN_NAME}"
DetailPrint LANG_ALBANIAN
${Case} "${LANGFILE_ARABIC_NAME}"
DetailPrint LANG_ARABIC
${EndSelect}

Related

Can Inno Setup Preprocessor be used to build a duplicated set of custom messages?

My installer caters for 20 partial translations. Partial translations have the bulk of the GUI in English and just certain parts in the respective languages. To acheive this with the installer I physically duplicate the default English isl file and rename the language name property to each of the twenty languages. Then Iuse those files in the installer script to add these languages to the drop-down list for the user to choose from.
What this means is that for my custom message I have to replicate them for each of the 20 languages. At the moment I am doing it manually but I wondered if this can be managed from now on using ISPP?
So we start with these custom messages:
; Automatic Backup Settings Wizard Page
English.pageAutoBackupTitle=Automatic Backup
English.pageAutoBackupDescription=Configure automatic backup settings.
English.lblBackupWhat=What to backup:
English.radBackupWhatNone=Don't perform any backup when the program shuts down
English.radBackupWhatComplete=Make a complete backup when the program shuts down
English.radBackupWhatEssential=Only make an essential backup when the program shuts down
English.lblBackupMode=How to backup:
English.radBackupModeAuto=Perform automatically when the program is shut down
English.radBackupModeManual=Prompt the user when the program is shut down
English.lblPromptMode=Also prompt to backup at the following intervals while the application is running:
English.cmbPromptModeItemNever=Never prompt to backup
English.cmbPromptModeItemDaily=Prompt to backup everyday
English.cmbPromptModeItemWeekly=Prompt to backup once a week
English.cmbPromptModeItemMonthly=Prompt to backup once a month
English.lblBackupFolder=Where to backup:
English.btnButtonBrowse=Browse ...
And we want to replicate them for the following languages identifiers:
Amharic
Aukan
Cebuano
Filipino
Gujarati
Gun
HaitianCreole
Lingala
Malagasy
Maltese
Punjabi
Saramaccan
Sesotho
Setswana
Sranantongo
Swahili
Tamil
Tsonga
Twi
Zulu
I know I don't need to add the custom messages because the installer would default to English, but then I get the barage of compiler warnings about missing message definitions.
The preprocessor has two mechanisms how to repeat some code, both having advantages and disadvantages.
User defined procedures
With the User defined procedures, the code that you want to repeat is a way more readable – you basically use the same syntax as if you write a normal script (even syntax highlighting will work in editors), you just use {#Var} syntax to inject the variable parts.
There's virtually no limit to how long the repeated code can be long.
But the code to cause the code to repeat is clumsier, as the procedures cannot take arguments, so you have to pass the variable parts via "global variables". But in your case, with a single parameter and no recursion, it's not a big limitation.
#sub DefaultMessages
{#Lang}.pageAutoBackupTitle=Automatic Backup
{#Lang}.pageAutoBackupDescription=Configure automatic backup settings.
{#Lang}.lblBackupWhat=What to backup:
; ...
#endsub
#define Lang
#expr Lang = "German"
#expr DefaultMessages
#expr Lang = "French"
#expr DefaultMessages
; ...
If you prefer, you can put the variable assignment and procedure call to the same line using a sequential evaluation operator (a comma):
#define Lang
#expr Lang = "German", DefaultMessages
#expr Lang = "French", DefaultMessages
; ...
User defined functions
User defined functions can take parameters, so from a programming perspective, they are cleaner – and allow a recursion (not relevant in this case).
But the code is not very readable. And there's an internal preprocessor stack limit, which makes the solution not scalable.
#define DefaultMessages(Lang) \
Lang + ".pageAutoBackupTitle=Automatic Backup" + NewLine + \
Lang + ".pageAutoBackupDescription=Configure automatic backup settings." + NewLine + \
Lang + ".lblBackupWhat=What to backup:" + NewLine \
; ...
#emit DefaultMessages("German")
#emit DefaultMessages("French")
; ...
For another example, that shows implementing the same functionality using both these approaches, see:
Inno Setup - Recurse sub directories without creating those same sub directories
I am adding this answer just so that users are aware of a better approach to my situation.
I was recently told this (quite by chance as my question was a different one) on the Inno Setup Support channel:
It loads the MessageFiles in order, followed by the messages in the
script. Last one wins.
And prefixed values win over un-prefixed values within the script as
well.
So you should put all your English defaults into an .isl file that is
listed before the language-specific .isl file. Anything not specified
in the real language file will use the defaults from the previous
files.
Or alternatively if you do not want to use .isl files for your custom
messages, then you can specify your English defaults without prefix
and your actual translations with the appropriate prefix directly in
your script, omitting anything not yet translated. Again, anything
that's missing a language-specific-prefixed value will default back
to the un-prefixed value.
If you do both things, then anything in the script (prefixed or not)
will always override anything in the .isl files.
So I simplified my custom messages file:
[CustomMessages]
; ==================================================================
; These are the default custom messages.
; They are used by:
; - Amharic
; - Aukan
; - Cebuano
; - English
; - Filipino
; - Gujarati
; - Gun
; - HaitianCreole
; - Lingala
; - Malagasy
; - Maltese
; - Punjabi
; - Saramaccan
; - Sesotho
; - Setswana
; - Sranantongo
; - Swahili
; - Tamil
; - Tsonga
; - Twi
; - Vietnamese
; - Zulu
pageAutoBackupTitle=Automatic Backup
pageAutoBackupDescription=Configure automatic backup settings.
lblBackupWhat=What to backup:
radBackupWhatNone=Don't perform any backup when the program shuts down
radBackupWhatComplete=Make a complete backup when the program shuts down
radBackupWhatEssential=Only make an essential backup when the program shuts down
lblBackupMode=How to backup:
radBackupModeAuto=Perform automatically when the program is shut down
radBackupModeManual=Prompt the user when the program is shut down
lblPromptMode=Also prompt to backup at the following intervals while the application is running:
cmbPromptModeItemNever=Never prompt to backup
cmbPromptModeItemDaily=Prompt to backup everyday
cmbPromptModeItemWeekly=Prompt to backup once a week
cmbPromptModeItemMonthly=Prompt to backup once a month
lblBackupFolder=Where to backup:
All language prefixed translations follow in the same file. Using this approach I don't actually need to use the #sub / #endsub mechanism after-all.

Implement a Hyperlink into an LangString

Actually im Trying with NSIS standard-libary to implement a Hyperlink into my LangString. But when I "only" write the Link into the LangString it dont get clickable.
I´m using an own .nsh do Build the LangStrings:
!undef LANG
!define LANG = "English"
!LANG_STRING "myTest" "search on google: https://google.com/"
In my nsis Main the Code-Snippet looks like:
!macro LANG_STRING NAME VALUE
LangString "${NAME}" "${LANG_${LANG}}" "${VALUE}"
!macroend
!macro LANG_LOAD lang
!insertmacro MUI_LANGUAGE "${lang}"
!verbose 1
!include "descriptions\${lang}.nsh"
!verbose 4
!undef LANG
!macroend
#Pages(here i build my pages up)
!insertmacro LANG_LOAD "English"
Is there a handling that i can make it possible and when yes where i must implement it.
There is no built-in support for this. NSIS does not try to detect URLs in strings and Windows controls are rather limited in older versions (before XP).
You can fake it with a RichEdit control but there is no built in WM_NOTIFY handler to launch the URL:
System::Call 'KERNEL32::LoadLibrary(t "MsftEdit")' ; 4.1+, Windows XP SP1 and later
FindWindow $9 "#32770" "" $HWNDPARENT
SendMessage $9 ${WM_GETFONT} 0 0 $8
System::Call 'USER32::CreateWindowExW(i ${WS_EX_TRANSPARENT}, w "RICHEDIT50W", p0, i ${DEFAULT_STYLES}|${WS_TABSTOP}|${ES_MULTILINE}|${ES_READONLY}, i 0, i 0, i 300, i 50, p $9, p 0, p 0, p 0)p.s'
Pop $9
SendMessage $9 ${WM_SETFONT} $8 1
!define /math EM_SETTEXTEX ${WM_USER} + 97
System::Call "USER32::SendMessage(p$9,i${EM_SETTEXTEX},*l0,ts)" '{\rtf1{Hello} {\field{\*\fldinst{HYPERLINK "https://stackoverflow.com/questions/tagged/nsis"}}{\fldrslt{SO link}}} {World}}'
It is possible to handle WM_NOTIFY on a nsDialogs page. On built-in pages you need a custom plug-in. You could perhaps ask the author of the ButtonEvent plug-in to add support for this message. If < XP SP1 support is needed you would have to use a older version of the RichEdit control and the EM_AUTOURLDETECT message instead, this means the URL must be visible in the text. The nsDialogs example in NSIS v3 does this.
Another alternative is the SysLink control but it suffers from the same WM_NOTIFY issue and is also limited to Windows XP and later, ComCtlv6 (XPStyle On) and is Unicode only. A different internal implementation exists on Windows 2000 but I'm not sure if it is documented.
The final alternative is the Linker plug-in. I have never tried it myself and you must create several labels if you want to mix links and normal text.

Issue with filepath name, possible corrupt characters

Perl and html, CGI on Linux.
Issue with file path name, being passed in a form field, to a CGI on server.
The issue is with the Linux file path, not the PC side.
I am using 2 programs,
1) program written years ago, dynamic html generated in a perl program, and presented to the user as a form. I modified by inserting the needed code to allow a the user to select a file from their PC, to be placed on the Linux machine.
Because this program already knew the filepath, needed on the linux side, I pass this filepath in a hidden form field, to program 2.
2) CGI program on Linux side, to run when form on (1) is posted.
Strange issue.
The filepath that I pass, has a very strange issue.
I can extract it using
my $filepath = $query->param("serverfpath");
The above does populate $filepath with what looks like exactly the correct path.
But it fails, and not in a way that takes me to the file open error block, but such that the call to the CGI script gives an error.
However, if I populate $filepath with EXACTLY the same string, via hard coding it, it works, and my file successfully uploads.
For example:
$fpath1 = $query->param("serverfpath");
$fpath2 = "/opt/webhost/ims/DOCURVC/data"
A comparison of $fpath1 and $fpath2 reveals that they are exactly equal.
A length check of $fpath1 and $fpath2 reveals that they are exactly the same length.
I have tried many methods of cleaning the data in $fpath1.
I chomp it.
I remove any non standard characters.
$fpath1 =~ s/[^A-Za-z0-9\-\.\/]//g;
and this:
my $safe_filepath_characters = "a-zA-Z0-9_.-/";
$fpath1 =~ s/[^$safe_filepath_characters]//g;
But no matter what I do, using $fpath1 causes an error, using $fpath2 works.
What could be wrong with the data in the $fpath1, that would cause it to successfully compare to $fpath2, yet not be equal, visually look exactly equal, show as having the exact same length, but not work the same?
For the below file open block.
$upload_dir = $fpath1
causes complete failure of CGI to load, as if it can not find the CGI (which I know is sometimes caused by syntax error in the CGI script).
$uplaod_dir = $fpath2
I get a successful file upload
$uplaod_dir = ""
The call to the cgi does not fail, it executes the else block of the below if, as expected.
here is the file open block:
if (open ( UPLOADFILE, ">$upload_dir/$filename" ))
{
binmode UPLOADFILE;
while ( <$upload_filehandle> )
{
print UPLOADFILE;
}
close UPLOADFILE;
$msgstr="Done with Upload: upload_dir=$upload_dir filename=$filename";
}
else
{
$msgstr="ERROR opening for upload: upload_dir=$upload_dir filename=$filename";
}
What other tests should I be performing on $fpath1, to find out why it does not work the same as its hard-coded equivalent $fpath2
I did try character replacement, a single character at a time, from $fpath2 to $fpath1.
Even doing this with a single character, caused $fpath1 to have the same error as $fpath2, although the character looked exactly the same.
Is your CGI perhaps running perl with the -T (taint mode) switch (e.g., #!/usr/bin/perl -T)? If so, any value coming from untrusted sources (such as user input, URIs, and form fields) is not allowed to be used in system operations, such as open, until it has been untainted by using a regex capture. Note that using s/// to modify it in-place will not untaint the value.
$fpath1 =~ /^([A-Za-z0-9\-\.\/]*)$/;
$fpath1 = $1;
die "Illegal character in fpath1" unless defined $fpath1;
should work if taint mode is your issue.
But it fails, and not in a way that takes me to the file open error block, but such that the call to the CGI script gives an error.
Premature end of script headers? Try running the CGI from the command line:
perl your_upload_script.cgi serverfpath=/opt/webhost/ims/DOCURVC/data

Retrieving and displaying the Disk Volume Serial number in NSIS

I am using NSIS(HM NISedit 2.0.3 for editor) for building setup(s) of my application.
I am having a requirement for one of my setups to use the Disk volume serial number(in Hexadecimal format).I also need to display the same in a message box.
Can someone please post me the complete piece of script for getting the Disk Volume serial number displaying it in a Message box?
This page describes how to retrieve the serial number in hex format:
http://nsis.sourceforge.net/Get_Disk_Volume_Serial_Number
So to do what you need include this function in your script:
Function ShowDiskVolumeSerialNumber
!define GetVolumeInformation "Kernel32::GetVolumeInformation(t,t,i,*i,*i,*i,t,i) i"
System::Call '${GetVolumeInformation ("$0",,${NSIS_MAX_STRLEN},.r0,,,,${NSIS_MAX_STRLEN})'
IntFmt $0 "%08X" $0
MessageBox MB_OK $0
FunctionEnd
and call it like this:
StrCpy $0 "C:\\"
Call ShowDiskVolumeSerialNumber
Obviously you can replace the drive letter with whatever you like but ensure it has a double trailing backslash .

80x86 Assembly - Very basic I/O program conversion to Linux from Windows

So my first day of Assembly class, and what do you know? My professor teaches everything on her Windows box, using Windows API calls, etc. which is fine except that I'm running Ubuntu on my box..
Basically, I'm hoping I can find either a workaround or some form of common-grounds in order for me to get my assignments done.
Today, our first programming assignment was to input two integers and output the sum. I followed my professor's code as follows:
.386
.model flat
ExitProcess PROTO NEAR32 stdcall, dwExiteCode:DWORD
include io.h
cr EQU 0dh
lf EQU 0ah
.stack 4096
.data
szPrompt1 BYTE "Enter first number: ", 0
szPrompt2 BYTE "Enter second number: ", 0
zLabel1 BYTE cr, lf, "The sum is "
dwNumber1 DWORD ? ; numbers to be added
dwNumber2 DWORD ?
szString BYTE 40 DUP (?) ; input string for numbers
szSum BYTE 12 DUP (0) ; sum in string form
szNewline BYTE cr,lf,0
.code ; start of main program code
_start:
output szPrompt1 ; prompt for ?rst number
input szString,40 ; read ASCII characters
atod szString ; convert to integer
mov dwNumber1,eax ; store in memory
output szPrompt2 ; repeat for second number
input szString,40
atod szString
mov dwNumber2,eax
mov eax,dwNumber1 ; first number to EAX
add eax,dwNumber2 ; add second number
dtoa szSum,eax ; convert to ASCII characters
output szLabel1 ; output label and results
output szSum
output szNewline
INVOKE ExitProcess,0 ; exit with return code 0
PUBLIC _start ; make entry point public
END ; end of source code
Simple and straightforward enough, yeah? So I turned it in today all linked up from the crappy school computers. And I completely understand all the concepts involved, however, I see 2 main issues here for if I actually want to assemble it on my box:
1) .model flat
2) ExitProcess PROTO NEAR32 stdcall, dwExiteCode:DWORD
And
Both of which I've heard are very Windows-specific. So my question is how can I mutate this code to be able to assemble on Linux?
Sorry If I'm missing any details, but I'll let you know if you need.
Thanks!
Assembly code is, generally speaking, almost always platform specific. Indeed, the very syntax varies between assemblers, even within the same hardware and OS platform!
You'll also probably have problems with that io.h there - I would bet it's making a lot of calls into win32 APIs.
I would recommend simply using wine, along with a copy of whatever assembler your professor is using, to run your professor's examples. If it can run things like Microsoft Office and Steam, it can certainly run some trivial example code :)

Resources