Bug 691440 - Windows bat files use relative paths and write to non-temp dir
Summary: Windows bat files use relative paths and write to non-temp dir
Status: RESOLVED FIXED
Alias: None
Product: Ghostscript
Classification: Unclassified
Component: Config/Install (show other bugs)
Version: 8.64
Hardware: PC Windows 7
: P4 minor
Assignee: Ken Sharp
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2010-07-03 12:33 UTC by Bjorn Martensson
Modified: 2014-02-17 04:47 UTC (History)
3 users (show)

See Also:
Customer:
Word Size: ---


Attachments
Suggested patch: use a Win9X-compatible construct instead of ‘%~dp0’ (13.97 KB, patch)
2010-07-27 19:41 UTC, SaGS
Details | Diff
Suggested patch: %TEMP%-related fixes (11.35 KB, patch)
2010-07-27 19:42 UTC, SaGS
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Bjorn Martensson 2010-07-03 12:33:32 UTC
The windows bat files e.g. ps2pdf.bat call other .bat files using paths relative to the current script. This doesn't resolve correctly if e.g. ps2pdf.bat is used in the windows explorer context menu (Right Click->open with->ps2pdf.bat).

To fix this, all calls from a bat file to other bat files or executables should prepend the current scripts path to the call e.g. in ps2pdf14.bat, instead of:
call ps2pdfxx %1 %2
it should be
call "%~dp0ps2pdfxx" %1 %2

Secondly, when using the context menu, the working dir is set to the windows system directory e.g. c:\windows\system. This is usually read only, so temporary files that are created by GS fails (e.g. _.at and _.at2). To fix this, I suggest prepending the windows temp dir in front of these files e.g. instead of 
echo -dCompatibilityLevel#1.4 >_.at
it would be
echo -dCompatibilityLevel#1.4 >"%TEMP%\_.at"

I would be happy to submit a patch, but couldn't find the right CVS repository.

I've fixed this and tested it on my own machine.

Thanks for a great program.
Bjorn
Comment 1 Hin-Tak Leung 2010-07-03 12:41:12 UTC
(In reply to comment #0)
> I would be happy to submit a patch, but couldn't find the right CVS repository.

http://git.ghostscript.com and http://svn.ghostscript.com both should work. git is a mirror and svn is authoritative .
Comment 2 Ken Sharp 2010-07-09 09:49:16 UTC
I was finally able to find a reference which stated that the %~d batch parameter was added to Windows in NT 4. That's sufficiently long ago that I fell comfortable with using it in the batch scripts. 

As noted in the submission log this does mean that Windows 3.x, 95 and 98 may not work with these scripts (I can't find any definitive statements about those versions of Windows), however those are sufficiently old that I don't think we need to worry about them.

The files have been updated in revision 11498, patch available here:

http://ghostscript.com/pipermail/gs-cvs/2010-July/011373.html

I have tested a number, but haven't been able to be completely exhaustive about it, more testing would be welcome.

Thanks very much for your contribution, if you feel you would like to submit a patch in future, then simply add it as an attachment to the bug report.
Comment 3 pipitas 2010-07-09 11:52:48 UTC
My worry with this would also have been the lacking support for older Windows systems. Personally, I'm using this trick in my batch files since a few years.

To check, if cmd.exe on your version of Window actually supports it, just run "for /?" and scroll down towards the end. Here is a list of all posible loop variable (%a, %b, %c,...) or positional parameter (%0, %1, %2,...) replacements (using "I" for letter or number):

%~I  : expand %I and remove all enclosing quotes (").
%~fI : expand %I to the full file name.
%~dI : show the drive name where %I is located.
%~pI : show the path for %I.
%~nI : show the name of %I without file suffix.
%~xI : show the suffix of %I.
%~sI : show the 'short' path for %I.
%~aI : show the file attributes of %I.
%~tI : show date+time of %I.
%~zI : show filesize of %I.

One can combine these like for example here:

%dpI : show drive letter with path for %I.
%nxI : show 'filename.suffix' for %I.
%fsI : show path with filename of %I, but completely the short version.

If you use these wisely, you can make .bat files work even using relative paths.
Comment 4 pipitas 2010-07-09 11:58:09 UTC
Sorry for the "noise" in comment #3. (I had this bug open in a browser tab since a few days, with the comment written, but for some reason not committed yet. When realizing that a click on the "Commit" was still missing, I just did it. Only after the fact I realized that comment #2 had appeared in the meanwhile confirming Bjorn's patch had been accepted).
Comment 5 SaGS 2010-07-27 19:31:33 UTC
There are a few problems with the adopted solution, and I will suggest 2 
patches for fixing them.

Patch order: the 2 patches are ‘successive’, in comment order. Please 
apply them in this order, otherwise patch won’t find the context it needs. 
There are hunks in the 1st patch that apply with a fuzz factor, that’s OK; 
the reasons are the ‘$Id$’s, patch does not know to un-expand them.


(A) %~dp0 does not work on Win95/98/ME, only on the NT-based versions

This is already mentioned in comments above. Fortunately there is an easy 
way to rectify the situation, so no need to cut off old Windows versions.

The technique used: ‘%0\..’ is the absolute/relative directory of the 
batch file, as specified by the user on the command line. The ‘..\’ removes 
the path component that precedes it, in this case the bat filename itself; 
it is not necessary for the respective component to represent an actual 
directory, it can be a file or not exist at all.

There is only one case when this does not work directly and has to be 
handled specially: if the user started the bat by typing its filename 
without any path AND the *.bat is not in the current directory. In this 
case (and only in this case) the command interpreter searches the %PATH%. 
The case is detected by ‘if not exist %0\..\self.bat’, and no path will 
be specified for subordinate batch files, so the command interpreter will 
be able to search them in the %PATH% the same as it did for the main bat.


(B) Temp files are (usually) written near, not inside, %TEMP%

With a default Windows installation, %TEMP% is set but without a trailing 
backslash. So ‘%TEMP%_at’ can be, for example, placed in the directory 
‘C:\Document and Settings\user\Local Settings’ and named ‘TEMP_at’.

The solution: use ‘%TEMP%.\_at’ instead. Here is how this works:

  - case 1: %TEMP% not set
    ‘%TEMP%.\_at’ becomes ‘.\_at’. Temp files are created in the current 
    directory as before.

  - case 2: %TEMP% set without a trailing ‘\’, for example ‘C:\TEMP’
    ‘%TEMP%.\_at’ becomes, for example, ‘C:\TEMP.\_at’. This is the same 
    as ‘C:\TEMP\_at’, because Windows IGNORES trailing periods.

  - case 3: %TEMP% set with a trailing ‘\’, for example ‘C:\TEMP\’
    ‘%TEMP%.\_at’ becomes, for example, ‘C:\TEMP\.\_at’, OK.


(C) A typo: a ‘>>’ was suppressed from ps2pdf.bat

    Modified: trunk/gs/lib/ps2pdf.bat
    ===================================================================
    --- trunk/gs/lib/ps2pdf.bat	2010-07-08 20:01:59 UTC (rev 11497)
    +++ trunk/gs/lib/ps2pdf.bat	2010-07-09 09:40:17 UTC (rev 11498)
    @@ -6,13 +6,13 @@
    ...
     :top
    -echo %1 >>_.at
    +echo %1 %TEMP%_.at       <== HERE
     shift
    ...

(D) It’s not impossible (but a bad idea) for %TEMP% to contain spaces

I noticed Windows to define %TEMP% using DOS 8.3 names by default. However, 
it is possible the user does differently, and also it’s possible to 
completely disable the creation of DOS 8.3 aliases on disk.
Comment 6 SaGS 2010-07-27 19:41:41 UTC
Created attachment 6565 [details]
Suggested patch: use a Win9X-compatible construct instead of ‘%~dp0’

Follow-up #1 to rev 11498: replace the use of ‘%~dp0’, available only on 
NT-based Windows, with ‘%0\..’ which is compatible with all Windows 
versions. A special test is also done to detect the situation when the bat 
was found by searching the %PATH%, in which case no path is specified for 
subordinate bat files to allow the command interpreter to look for them 
on the %PATH% as it did for the master bat.
Comment 7 SaGS 2010-07-27 19:42:26 UTC
Created attachment 6566 [details]
Suggested patch: %TEMP%-related fixes

Follow-up #2 to rev 11498:
(i)   By using the construct ‘%TEMP%_at’, temp files may (and often they 
      do, because by default %TEMP% does not have a trailing ‘\’) go in 
      the %TEMP%’s parent, not inside %TEMP%. Use ‘%TEMP%.\_at’ instead.
(ii)  Fix a typo in ps2pdf.bat, the removal of a ‘>>’.
(iii) Enclose temp filenames in quotes, just in case %TEMP% contains 
      spaces or other separators.
Comment 8 Ken Sharp 2010-07-28 07:43:55 UTC
(In reply to comment #5)

Thanks for spotting the typo in ps2pdf.bat, I've committed the fix for that as revision 11545.

As for the remaining issues; as we keep on saying, batch programming in Windows is a difficult area, its not possible to do a really good job. 

The batch files are provided as a convenience, if they don't work you can still run Ghostscript from the command line. Or, of course, write your own batch files. The main aim here is to provide something which works reasonably well for most users, and which we're comfortable in supporting. I don't want to take on the job of endlessly patching a rickety structure to try and retain compatibility with old versions of Windows.

Microsoft hasn't supported Windows 98 or ME since July 2006, four years bow, so I don't particularly feel we need to worry about it. I'd rather move on and use the new capabilities.

If I hear howls of complaint from the user community I'll have to rethink that position of course.
Comment 9 SaGS 2010-07-28 10:07:03 UTC
The temp-files-miss-the-%TEMP%-folder issue (comment #5 point (B)) affects 
by default all Windows versions, old and new, because the %TEMP% environment 
variable does not normally include a trailing backslash.

The %TEMP%-containing-spaces (comment #5 point (D)) affects equally well 
all versions, maybe with an extra for the NT-based ones including those 
current. The cause of this extra is that it’s possible to completely 
suppress, for performance reasons, the creation of DOS 8.3 filenames on 
NTFS volumes (‘fsutil behavior set disable8dot3 1’); then spaces become 
almost unavoidable (‘...\Local Setting\...’ and such).

Now, my opinion about comment #5 point (A) may be biased, as I still use 
sometimes those old Windows versions (Win98 often enough, Win95 rarely).

Of course, the decision is yours. You may decide to do nothing. But if you 
decide to fix at least comment #5 point (B) (and eventually (D)), what is
easier and takes less time:

    (i)  apply the 2 patches as they are;
or  (ii) redo and retest something similar to attachment #6566 [details]?

(Okay, okay, I confess: I’m evil.)
Comment 10 SaGS 2010-09-02 16:19:26 UTC
Unfortunately there’s a 5th problem coming from the committed patch, 
bigger than all the others together.

(E) Once Ghostscript installed with default options, most (all?) batch 
    files dont’t work at all even with current versions of Windows. 
    Tested for now on WinXP-SP3 which is supported by Microsoft.

    Examples:
    ==================================================================
    C:\Program Files\gs\gs8.71\lib>ps2pdf input.ps output.pdf
    'C:\Program' is not recognized as an internal or external command,
    operable program or batch file.
    
    C:\Program Files\gs\gs8.71\lib>
    ==================================================================
    C:\Program Files\gs\gs8.71\lib>ps2ascii file.ps file.txt
    'C:\Program' is not recognized as an internal or external command,
    operable program or batch file.
    '-q' is not recognized as an internal or external command,
    operable program or batch file.
    
    C:\Program Files\gs\gs8.71\lib>
    ==================================================================
    No output file created in either of these cases.

    (Note: I’m using the current revision of GS; the ‘8.71’ appears 
    there because I installed 8.71 from code.google.com, then replaced 
    all files with ones resulting from a default build of TRUNK 
    revision 11680 and modified the registry appropriately.)

Cause: the ‘%~dp0’ expands to the drive+path, including any spaces or 
command line separators that happen to be there, without quotes around 
the whole thing. So any ‘call %~dp0file.bat’ line in the *.BAT results 
in ‘call C:\Program Files\C:\Program Files\gs\gs8.71\lib\file.bat’, 
and the command interpreter looks for C:\Program.(bat|com|exe) which 
(hopefully! - security risk) does not exist.

Batch files that call others for something essential (like ‘ps2pdf.bat’) 
don’t work because the subordinate BAT files are not found. Those that 
don’t rely on others (like ‘ps2ascii.bat’) don’t work in general because 
they don’t manage to execute ‘gssetgs.bat’ and %GSC% remains undefined.

My patch from comment #6 does not have this problem, because if quotes 
are needed they are already present in ‘%0’ and are preserved. Of course 
I would prefer committing it. Failing that, all instances of ‘%dp0’ 
need to be quoted. Failing this also, and to really stick with 
comment #8, please revert the committed patch. The OP can solve the 
problem with the directory by defining the PATH environment variable; 
the trouble with temp files cannot be solved so easily (but Windows 
Script Host or Windows PowerShell should come to the rescue...).

I’m reopening this bug so this new issue does not get forgotten. It’s 
not the same as the original one, but all discussion and patches are here.
Comment 11 Ken Sharp 2010-09-03 06:54:45 UTC
revision 11684 should solve this. Path expansion (whether from environment variables or magic %~d) are guarded with quotes.

The %TEMP% use gets a trailing path separator. Doubled separators don't seem to cause a problem so this seems safe.