NT Login Scripting X (NuTLSX) Language Spec
The Background
NuTLS (pronounced "nut - elz") was developed to solve a dilemma. The dilemma was generated because I did not want to learn KixStart, the scripting language included with NT4, and did not want involved Perl scripts that people after me would have to decipher.
NuTLS was designed to be a simple, semi-powerful login scripting language for Windows NT 4.0. Version 1.0 was written in about 14 hours in the Spring of 1998, using Microsoft Visual BASIC 5.0, and conforming to the Win32 API making it (theoretically) compatible with Win32s for Win3.11, Win95, WinNT4, and Win98/WinNT5 and beyond.
NuTLSX is is the latest generation of NuTLS.
NuTLSX (pronounced "nut - elz - ex") is a near-complete rewrite of the NuTLS language core taking about 21 hours in the Winter of 2002. New features have been added, code has been optimized, and the parser can now run as either a stand-alone application (as NuTLS did) or as a service (on Windows NT,2k,XP, etc.). The only thing NuTLS could do that NuTLSX can't is lie (don't ask), but there is much that NuTLSX can do that NuTLS could not.
The Theories and Assumptions
Preface
There is no preprocessor, and no I will not institute one, so don't even bother asking. Environment variables are expanded in a preprocessor-esque way.
Case does not matter.
If you get a "Bad Command Line Format" message when you run NuTLSX, that's because you didn't follow the Command Line Arguments docs!
Statement Theory
NuTLSX assumes that every line contains only one statement. Thus the following is invalid:
mount \\myserver\myshare X: mount \\myserver\anothershare Y:
Although the syntax for both mount commands is valid, the combination of them is illegal, thus to get the desired effect, the following syntax is appropriate:
mount \\myserver\myshare X: mount \\myserver\anothershare Y:
This law does not apply to comments. Comments are denoted by a semicolon (;) or a sharp (#), and mark the rest of the line as a non-executable comment. Thus the following block of code is perfectly legal:
mount \\myserver\myshare X: #mount the myshare on to the local drive X: ;This whole line is a comment because I said so
In the case of conditionals (which I will discuss later), each part of the conditional is treated as a statement, thus the following is illegal:
if (i_stubbed_my_toe) then call_my_mom else leave_me_alone endif
To make this a valid expression, the following syntax needs to be applied:
if (i_stubbed_my_toe) then call_my_mom else leave_me_alone endif
Leading, trailing, and logically embedded spaces, tabs, carriage returns, linefeeds, are ignored by the language, so feel free to use them as you see fit to make your code more readable.
Statements that have pathnames in their syntax, should be encased in quotes, but do not have to be.
The reason that pathnames do not have to be encased in quotes, is that the processor looks at an entire line as a statement, and that statement is then broken into statement fragments based upon the syntax of the statement. For example: the syntax for the mount command is: mount path_to_mountable_share drive: , so the processor sees the keyword "mount", and knows that there will be two fragments to the statement, a path and a drive. Each legal fragment has a different specification. Below is a list of all valid fragments and their specification.
| path | a path fragment is looked at in a backwards fashion, so that embedded spaces do not matter. Thus the following two paths are treated virtually the same:
c:\program files\accessories c:\progra~1\accessories A path fragment can be a drive path or an UNC network path such as: \\myserver\myshare |
| dump | a dump fragment is simply taken en masse and "dumped" to the operating system, or into another function. It is not parsed in any way (except to expand environment variables) before hand. |
| drive | a drive fragment is also looked at backwards. A drive is defined as a colon (:), preceded by a valid letter (A-Z), which is preceded by whitespace (TAB or SPACE) or nothing.. |
| expression | an expression fragment MUST be encased in parenthesis, and is defined as all data, text, numbers or what not inside of the parenthesis. |
| constant | some commands have constants that you may pass to them. (More on this later) |
Error Theory
Errors occur on four levels. The following chart should clear this matter up:
| Constant Name | Numeric Value | Description |
| ERROR_TRIVIAL | 0 | Yeah, so what? |
| ERROR_SERIOUS | 1 | Hmmm, this is not good. |
| ERROR_SEVERE | 2 | Oh dear, this is very bad. I need a raise. |
| ERROR_SYSTEM | 3 | Syntax is wrong. You made an oopsie... Probably. |
Non-system errors are generated by everything from a mistyped keyword (ERROR_TRIVIAL), to an invalid path (ERROR_SERIOUS), to bad logic (ERROR_SEVERE).
Proper use of the -error and -noise statements are essential to get these nasties handled properly.
| error off | No non-system error will halt execution. DEFAULT |
| error 0 | Any error will halt script execution |
| error 1 | Any SERIOUS or SEVERE error will halt execution |
| error 2 | Any SEVERE error will halt execution |
Logging Theory
Every action is logged twice, once before the action (signaling an attempt), and once after the action (signaling failure or success).
Environment Variable Theory
NuTLS can read predefined environment variables in a %VARNAME% format. This special fragment can be placed inside of paths, expressions, dumps, etc. If VARNAME does not exist, nothing is put in it's place. For example, if BOB is not a valid environment variable, then
c:\%BOB% would be the same as c:\. But if BOB was equal to WINDOWS, then c:\%BOB% would be the same as c:\windows.
The special variable %!USER!% will always be the currently logged in user.
The Guts
Mounting Drives
The mount command has been implemented to aid in the mounting of remotely shared directories / volumes.
NOTE: Mounting can only occur at the root-share level. For example, if there is a share on a computer called myserver, and the share is named myshare, and inside of myshare there is a folder called anotherShare, you cannot mount a drive directly to anotherShare, you can only mount it to myshare. This is a restriction of Windows itself, not NutLS. For technical reasons, if you use my mount command on a folder inside of a share, it will mount the share instead. You're welcome.
The syntax of the mount command is as follows (in statement fragment notation):
mount path drive
If path is invalid, a SERIOUS error is generated, if drive already exists then a TRIVIAL error is generated.
Unmounting Drives
The unmount command has been implemented to reverse the effects of the mount command.
The syntax of the unmount command is as follows:
unmount drive
Where drive is a letter followed by a colon (:)
Executing External Programs
The exec command is used to execute valid executables (.exe, .com, .bat). There may be a problem executing the program, depending on system restrictions.
NOTE: It is not advised that you use this command. It is provided for use of people who know what they are doing. Exec'ing programs can compromise network security and general system integrity.
CAUTION: NuTLS DOES NOT wait for the execution of an exec'd program to finish. As soon as the program is spawned, execution of the script continues. The syntax is as follows:
exec dump
Everything after the exec keyword (except comments of course), is simply dumped to the Operating System. If the Operating System refuses the stuff in dump for any reason, a SERIOUS error is generated. Any environment variables in the dump will be properly expanded.
Executing Another NuTLSX Script
The script command is used to execute another NuTLSX script. The syntax is:
script dump
Everything after the script keyword (except comments of course. Any environment variables in the dump will be properly expanded, also) will be tossed to a stand-alone instance of NuTLSX. As such variables, conditionals, etc WILL NOT step on or override the main program. Supplying the path to a NuTLSX script would be the same as saying:
exec "c:\program files\nutlsx\nutlsx.exe" -nosvc -file dump
Replacing the Running NuTLSX Script
If you would like to replace the running script with another one, that's what the replace command is for. The syntax is:
replace path
Where path is the file path to the new script file. The current script will run through before it is replaced. CAUTION: It is strongly recommended that any use of replace be at the very end of a script, and that a script that is going to be replaced should NOT set a frequency (unless it's to 0)!
Message Boxes (IOW: Forcing User Interaction)
It may be necessary to alert the user to certain events for any one of a zillion reasons, so I've implemented a small subset of the Windows message boxes. When a message box is created it pauses execution of the script until the user clears the box (usually by clicking the "Ok" button). The syntax is as follows:
msg "dump" constant
The text inside of the quotes is what is displayed in the message box. The default constant (if none is specified) is MSG_OK. The constant is a text or numeric value that matches an item in the following table.
| Constant | Numeric Value | Description |
| MSG_OK | 0 | Default. Standard no-frills Ok box |
| MSG_OKCANCEL | 1 | Box with OK and CANCEL buttons |
| MSG_YESNO | 4 | Box with YES and NO buttons |
| MSG_CRITICAL | 16 | Ok box that generates a Windows Critical Stop Event |
| MSG_QUESTION | 32 | Ok box that generates a Windows Question (Query) Event |
| MSG_EXCLAMATION | 48 | Ok box that generates a Windows Exclamation (Warning) Event |
| MSG_INFORMATION | 64 | Ok box that generate a Windows Information Event |
Expression Analysis (Conditionals)
Every decent language has if statements, and NuTLS is no exception. All of these statements take the generic form:
if (expression) statement block else statement block endif
If statements can be cascaded (nested if you will) inside other if's or else's, as long as there is an endif for every if.
NOTE: If you "forget" an endif, your script will act very odd. It will execute without any extra error or flag being generated. If you turn logging on, each if, else, and endif is logged.
NOTE: The concept of an "else if" or "elsif" does not exist. Sorry. Maybe in the future.
Several forms of if exist, so here's yet another table:
| Conditional | True If |
| ifexists (pathtofile) | if pathtofile exists (and is a file) |
| ifenviron (something = somethingelse) | if sometime and somethingelse are equal. May be variables, may not be. 1 = 1 would work. |
If any expression value is text, it should be enclosed in quotes. For example:
ifenviron(%!USER!% = "BOB") then msg "Hi Bob" endif
Also, if you want to tell if anyone is logged in or not you can say:
ifenviron(%!USER!% = "") then # No one is logged in else # Someone is logged in endif
Forcing Errors
There may be times when you want your script to generate an error (usually in a conditional). This is handled with the error command. The keyword error is then followed by the error state. The error state can be: trivial, serious, or severe. You cannot force a system error. Example of this would be:
error severe "dump"
The text encased in quotes will be the message contained in the logfile and/or message box.
Forcing a User to Logoff and/or the System to Reboot
In the event you want to force a user to logoff or reboot the system, the exit command is used.
| exit Command | Description |
| logoff or nothing | Unforced system request to logoff the current user |
| forcelogoff | Forced system request to logoff the current user |
| reboot | Unforced system request to logoff the current user and reboofilt |
| reset or forcereboot | Forced system request to logoff the current user and reboot |
When called without options or as exit logoff, NuTLSX will simply issue a logoff signal to Windows. User applications running at the time will be asked politely to close, and they may ask the user to save their work, etc. CAUTION: Applications may also politely tell Windows to screw off, and thus terminate the logoff process.
In the event that you need to be sure the logoff process is not trumped by an ill-written application (or ill-mannered user), issuing an exit forcelogoff command will send a different logoff signal to Windows. Well-written user applications may still ask the user to save their work, but each application is assigned a time-out and after that period expires it is forcibly closed.
If you want the computer to reboot, but want to politely tell running applications about it, you can issue the exit reboot command. This is equivalent to the exit logoff followed by a reboot, and as such the same caveats apply.
If you want to make sure the computer will reboot, the exit reset (or exit forcereboot) command is equivalent to the exit forcelogoff command followed by a reboot.
Creating an Emptyish File
If the need arises that you need a file to exist where one currently does not, the create command will do that for you. The contents of that file will be a number. Don't worry about the number, it's just there to prevent Windows' over-zealous "clean up" routines from erasing it because it's "empty". Syntax is as expected:
create pathtofile
Copying or Moving a File
If you want to copy a file, the copy command is ironically used. If you want to move a file (which is a copy followed by a delete of the original if the copy succeeded), the move command is used. Syntax is the same for both:
copy pathtofile -> pathtodestination move pathtofile -> pathtodestination
Yes, that's a dash followed by a greater-than in the middle. How quaint, I know.
Deleting or Recycling a File
In the event that you want to delete a file, the amazingly-named delete command is what you're looking for. If you're uncomfortable with wiping bits from the system hastily, the recycle command is around to help ease the discomfort. The syntax for either is the same:
delete filepath recycle filepath
CAUTION: The recycle command will place the relevant file in the "Recycle Bin" for the user who NuTLSX is running as. This user may or may not have an interactive account. Strange things may happen. I don't know. You've been warned.
Setting the Location of the Logfile
The logfile, by default is called nutlsx.log and resides in the same folder as the NuTLSX.exe that is writing to it. If this isn't good enough for you, the -log command may be used. The syntax is pretty easy:
-log pathtologfile
CAUTION: I strongly recommend this be your first command in any script. Actually, I strongly recommend you never touch this but that's another matter.
Annoying the User With Error Messages
By default, NuTLSX will log any Bad Things that might happen and silently exit. If you'd rather a message box pop up and tell the user of the problem, you may do so with the -noise command. You can use this throughout your script by turning -noise on (or just -noise) where you want to start possibly annoying the user with error messages, and then a -noise off when you're done... For now *evil cackle*. Anyhow, the syntax has already been mentioned, so I will spare the poor horse.
Determining How Often You Want the Script to Run
Sometimes you just want a single run of a script and then exit. Sometimes (especially when running as a service) you don't. If you specify a frequency, then the script will be re-run every that often. Syntax is pretty predictable:
frequency seconds
NOTE: The timing mechanism used isn't perfect. In fact, it's far from perfect. But it won't suck CPU cycles like more better mechanisms. For this reason there may be as much as one full second's deviance from what you specify. For the curious, this is because there is a top and a bottom to every second, and whether the timer "fires" at the top or the bottom of any given second is determined by so many different things that I could write a book about it. So rest assured, sometime under one full second after the number of seconds you specify, the script will be run. If you need precise seconds, I recommend using a better operating system, because Windows is a horribly imprecise beast.
Setting the Error Level
Errors occur on four levels. The following chart should clear this matter up:
| Constant Name | Numeric Value | Description |
| ERROR_TRIVIAL | 0 | Yeah, so what? |
| ERROR_SERIOUS | 1 | Hmmm, this is not good. |
| ERROR_SEVERE | 2 | Oh dear, this is very bad. I need a raise. |
| ERROR_SYSTEM | 3 | Syntax is wrong. You made an oopsie... Probably. |
Non-system errors are generated by everything from a mistyped keyword (ERROR_TRIVIAL), to an invalid path (ERROR_SERIOUS), to bad logic (ERROR_SEVERE).
-error off No non-system error will halt execution. DEFAULT -error 0 Any error will halt script execution -error 1 Any SERIOUS or SEVERE error will halt execution -error 2 Any SEVERE error will halt execution
NOTE: System errors will always halt the program.
Command Line Arguments
Syntax: NuTLSX.exe [-install|-uninstall|-debug] [-nosvc [-file filepath]]
All command line options are optional. However simply executing the program assumes it's installed as a service and will start that service.
- -install | -uninstall | -debug
These options are used for manipulating the Windows "services" system (Windows NT 4, 2000, XP and later, only) and are required by the operating system. -install registers NuTLSX with Windows services, -uninstall unregisters it, and -debug tosses the Windows services system into debug mode (NOTE: I've found this mostly useless.)
- -nosvc
In the event that you don't want to run NuTLSX as a service, or are on an older Windows that does not support them, you can just execute NuTLSX in "stand-alone" mode.
- -file filepath
If you're using the -nosvc option, you may follow that with -file filepath where filepath is the path to a NuTLSX script. If this is not set it defaults to nutlsx.scr in the same folder as the NuTLSX.exe that's being executed. If filepath has embedded spaces, it should be encased in quotes.
Written 1998,2002,2006 Matthew Keller