dos-w32.el 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. ;; dos-w32.el --- Functions shared among MS-DOS and W32 (NT/95) platforms
  2. ;; Copyright (C) 1996, 2001-2012 Free Software Foundation, Inc.
  3. ;; Maintainer: Geoff Voelker <voelker@cs.washington.edu>
  4. ;; Keywords: internal
  5. ;; Package: emacs
  6. ;; This file is part of GNU Emacs.
  7. ;; GNU Emacs is free software: you can redistribute it and/or modify
  8. ;; it under the terms of the GNU General Public License as published by
  9. ;; the Free Software Foundation, either version 3 of the License, or
  10. ;; (at your option) any later version.
  11. ;; GNU Emacs is distributed in the hope that it will be useful,
  12. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. ;; GNU General Public License for more details.
  15. ;; You should have received a copy of the GNU General Public License
  16. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  17. ;;; Commentary:
  18. ;; Parts of this code are duplicated functions taken from dos-fns.el
  19. ;; and winnt.el.
  20. ;;; Code:
  21. ;; Use ";" instead of ":" as a path separator (from files.el).
  22. (setq path-separator ";")
  23. (setq minibuffer-history-case-insensitive-variables
  24. (cons 'file-name-history minibuffer-history-case-insensitive-variables))
  25. ;; Set the null device (for compile.el).
  26. (setq null-device "NUL")
  27. ;; For distinguishing file types based upon suffixes.
  28. (defcustom file-name-buffer-file-type-alist
  29. '(("[:/].*config.sys$" . nil) ; config.sys text
  30. ("\\.\\(obj\\|exe\\|com\\|lib\\|sys\\|bin\\|ico\\|pif\\|class\\)$" . t)
  31. ; MS-Dos stuff
  32. ("\\.\\(dll\\|drv\\|386\\|vxd\\|fon\\|fnt\\|fot\\|ttf\\|grp\\)$" . t)
  33. ; Windows stuff
  34. ("\\.\\(bmp\\|wav\\|avi\\|mpg\\|jpg\\|tif\\|mov\\|au\\)$" . t)
  35. ; known binary data files
  36. ("\\.\\(arc\\|zip\\|pak\\|lzh\\|zoo\\)$" . t)
  37. ; Packers
  38. ("\\.\\(a\\|o\\|tar\\|z\\|gz\\|taz\\|jar\\)$" . t)
  39. ; Unix stuff
  40. ("\\.sx[dmicw]$" . t) ; OpenOffice.org
  41. ("\\.tp[ulpw]$" . t) ; borland Pascal stuff
  42. ("[:/]tags$" . nil) ; emacs TAGS file
  43. )
  44. "Alist for distinguishing text files from binary files.
  45. Each element has the form (REGEXP . TYPE), where REGEXP is matched
  46. against the file name, and TYPE is nil for text, t for binary."
  47. :type '(repeat (cons regexp boolean))
  48. :group 'dos-fns
  49. :group 'w32)
  50. ;; Return the pair matching filename on file-name-buffer-file-type-alist,
  51. ;; or nil otherwise.
  52. (defun find-buffer-file-type-match (filename)
  53. (let ((alist file-name-buffer-file-type-alist)
  54. (found nil))
  55. (let ((case-fold-search t))
  56. (setq filename (file-name-sans-versions filename))
  57. (while (and (not found) alist)
  58. (if (string-match (car (car alist)) filename)
  59. (setq found (car alist)))
  60. (setq alist (cdr alist)))
  61. found)))
  62. ;; Don't check for untranslated file systems here.
  63. (defun find-buffer-file-type (filename)
  64. (let ((match (find-buffer-file-type-match filename))
  65. (code))
  66. (if (not match)
  67. (default-value 'buffer-file-type)
  68. (setq code (cdr match))
  69. (cond ((memq code '(nil t)) code)
  70. ((and (symbolp code) (fboundp code))
  71. (funcall code filename))))))
  72. (setq-default buffer-file-coding-system 'undecided-dos)
  73. (defun find-buffer-file-type-coding-system (command)
  74. "Choose a coding system for a file operation in COMMAND.
  75. COMMAND is a list that specifies the operation, an I/O primitive, as its
  76. CAR, and the arguments that might be given to that operation as its CDR.
  77. If operation is `insert-file-contents', the coding system is chosen based
  78. upon the filename (the CAR of the arguments beyond the operation), the contents
  79. of `untranslated-filesystem-list' and `file-name-buffer-file-type-alist',
  80. and whether the file exists:
  81. If it matches in `untranslated-filesystem-list':
  82. If the file exists: `undecided'
  83. If the file does not exist: `undecided-unix'
  84. If it matches in `file-name-buffer-file-type-alist':
  85. If the match is t (for binary): `no-conversion'
  86. If the match is nil (for dos-text): `undecided-dos'
  87. Otherwise:
  88. If the file exists: `undecided'
  89. If the file does not exist default value of `buffer-file-coding-system'
  90. Note that the CAR of arguments to `insert-file-contents' operation could
  91. be a cons cell of the form \(FILENAME . BUFFER\), where BUFFER is a buffer
  92. into which the file's contents were already read, but not yet decoded.
  93. If operation is `write-region', the coding system is chosen based upon
  94. the value of `buffer-file-coding-system' and `buffer-file-type'. If
  95. `buffer-file-coding-system' is non-nil, its value is used. If it is
  96. nil and `buffer-file-type' is t, the coding system is `no-conversion'.
  97. Otherwise, it is `undecided-dos'.
  98. The two most common situations are when DOS and Unix files are read
  99. and written, and their names do not match in
  100. `untranslated-filesystem-list' and `file-name-buffer-file-type-alist'.
  101. In these cases, the coding system initially will be `undecided'. As
  102. the file is read in the DOS case, the coding system will be changed to
  103. `undecided-dos' as CR/LFs are detected. As the file is read in the
  104. Unix case, the coding system will be changed to `undecided-unix' as
  105. LFs are detected. In both cases, `buffer-file-coding-system' will be
  106. set to the appropriate coding system, and the value of
  107. `buffer-file-coding-system' will be used when writing the file."
  108. (let ((op (nth 0 command))
  109. (binary nil) (text nil)
  110. (undecided nil) (undecided-unix nil)
  111. target target-buf)
  112. (cond ((eq op 'insert-file-contents)
  113. (setq target (nth 1 command))
  114. ;; If TARGET is a cons cell, it has the form (FILENAME . BUFFER),
  115. ;; where BUFFER is a buffer into which the file was already read,
  116. ;; but its contents were not yet decoded. (This form of the
  117. ;; arguments is used, e.g., in arc-mode.el.) This function
  118. ;; doesn't care about the contents, it only looks at the file's
  119. ;; name, which is the CAR of the cons cell.
  120. (when (consp target)
  121. (setq target-buf
  122. (and (bufferp (cdr target))
  123. (buffer-name (cdr target))))
  124. (setq target (car target)))
  125. ;; First check for a file name that indicates
  126. ;; it is truly binary.
  127. (setq binary (find-buffer-file-type target))
  128. (cond (binary)
  129. ;; Next check for files that MUST use DOS eol conversion.
  130. ((find-buffer-file-type-match target)
  131. (setq text t))
  132. ;; For any other existing file, decide based on contents.
  133. ((or
  134. (file-exists-p target)
  135. ;; If TARGET does not exist as a file, replace its
  136. ;; base name with TARGET-BUF and try again. This
  137. ;; is for jka-compr's sake, which strips the
  138. ;; compression (.gz etc.) extension from the
  139. ;; FILENAME, but leaves it in the BUFFER's name.
  140. (and (stringp target-buf)
  141. (file-exists-p
  142. (expand-file-name target-buf
  143. (file-name-directory target)))))
  144. (setq undecided t))
  145. ;; Next check for a non-DOS file system.
  146. ((untranslated-file-p target)
  147. (setq undecided-unix t)))
  148. (cond (binary '(no-conversion . no-conversion))
  149. (text '(undecided-dos . undecided-dos))
  150. (undecided-unix '(undecided-unix . undecided-unix))
  151. (undecided '(undecided . undecided))
  152. (t (cons (default-value 'buffer-file-coding-system)
  153. (default-value 'buffer-file-coding-system)))))
  154. ((eq op 'write-region)
  155. (if buffer-file-coding-system
  156. (cons buffer-file-coding-system
  157. buffer-file-coding-system)
  158. ;; Normally this is used only in a non-file-visiting
  159. ;; buffer, because normally buffer-file-coding-system is non-nil
  160. ;; in a file-visiting buffer.
  161. (if buffer-file-type
  162. '(no-conversion . no-conversion)
  163. '(undecided-dos . undecided-dos)))))))
  164. (modify-coding-system-alist 'file "" 'find-buffer-file-type-coding-system)
  165. (defun find-file-binary (filename)
  166. "Visit file FILENAME and treat it as binary."
  167. (interactive "FFind file binary: ")
  168. (let ((file-name-buffer-file-type-alist '(("" . t))))
  169. (find-file filename)))
  170. (defun find-file-text (filename)
  171. "Visit file FILENAME and treat it as a text file."
  172. (interactive "FFind file text: ")
  173. (let ((file-name-buffer-file-type-alist '(("" . nil))))
  174. (find-file filename)))
  175. (defun find-file-not-found-set-buffer-file-coding-system ()
  176. (with-current-buffer (current-buffer)
  177. (let ((coding buffer-file-coding-system))
  178. ;; buffer-file-coding-system is already set by
  179. ;; find-operation-coding-system, which was called from
  180. ;; insert-file-contents. All that's left is to change
  181. ;; the EOL conversion, if required by the user.
  182. (when (and (null coding-system-for-read)
  183. (or inhibit-eol-conversion
  184. (untranslated-file-p (buffer-file-name))))
  185. (setq coding (coding-system-change-eol-conversion coding 0))
  186. (setq buffer-file-coding-system coding))
  187. (setq buffer-file-type (eq buffer-file-coding-system 'no-conversion)))))
  188. ;;; To set the default coding system on new files.
  189. (add-hook 'find-file-not-found-functions
  190. 'find-file-not-found-set-buffer-file-coding-system)
  191. ;;; To accommodate filesystems that do not require CR/LF translation.
  192. (defvar untranslated-filesystem-list nil
  193. "List of filesystems that require no CR/LF translation when reading
  194. and writing files. Each filesystem in the list is a string naming
  195. the directory prefix corresponding to the filesystem.")
  196. (defun untranslated-canonical-name (filename)
  197. "Return FILENAME in a canonicalized form for use with the functions
  198. dealing with untranslated filesystems."
  199. (if (memq system-type '(ms-dos windows-nt cygwin))
  200. ;; The canonical form for DOS/W32 is with A-Z downcased and all
  201. ;; directory separators changed to directory-sep-char.
  202. (let ((name nil))
  203. (setq name (mapconcat
  204. (lambda (char)
  205. (if (and (<= ?A char) (<= char ?Z))
  206. (char-to-string (+ (- char ?A) ?a))
  207. (char-to-string char)))
  208. filename nil))
  209. ;; Use expand-file-name to canonicalize directory separators, except
  210. ;; with bare drive letters (which would have the cwd appended).
  211. ;; Avoid expanding names that could trigger ange-ftp to prompt
  212. ;; for passwords, though.
  213. (if (or (string-match "^.:$" name)
  214. (string-match "^/[^/:]+:" name))
  215. name
  216. (expand-file-name name)))
  217. filename))
  218. (defun untranslated-file-p (filename)
  219. "Return t if FILENAME is on a filesystem that does not require
  220. CR/LF translation, and nil otherwise."
  221. (let ((fs (untranslated-canonical-name filename))
  222. (ufs-list untranslated-filesystem-list)
  223. (found nil))
  224. (while (and (not found) ufs-list)
  225. (if (string-match (concat "^" (car ufs-list)) fs)
  226. (setq found t)
  227. (setq ufs-list (cdr ufs-list))))
  228. found))
  229. (defun add-untranslated-filesystem (filesystem)
  230. "Add FILESYSTEM to the list of filesystems that do not require
  231. CR/LF translation. FILESYSTEM is a string containing the directory
  232. prefix corresponding to the filesystem. For example, for a Unix
  233. filesystem mounted on drive Z:, FILESYSTEM could be \"Z:\"."
  234. ;; We use "D", not "f", to avoid confusing the user: "f" prompts
  235. ;; with a directory, but RET returns the current buffer's file, not
  236. ;; its directory.
  237. (interactive "DUntranslated file system: ")
  238. (let ((fs (untranslated-canonical-name filesystem)))
  239. (if (member fs untranslated-filesystem-list)
  240. untranslated-filesystem-list
  241. (setq untranslated-filesystem-list
  242. (cons fs untranslated-filesystem-list)))))
  243. (defun remove-untranslated-filesystem (filesystem)
  244. "Remove FILESYSTEM from the list of filesystems that do not require
  245. CR/LF translation. FILESYSTEM is a string containing the directory
  246. prefix corresponding to the filesystem. For example, for a Unix
  247. filesystem mounted on drive Z:, FILESYSTEM could be \"Z:\"."
  248. (interactive "fUntranslated file system: ")
  249. (setq untranslated-filesystem-list
  250. (delete (untranslated-canonical-name filesystem)
  251. untranslated-filesystem-list)))
  252. ;;; Support for printing under DOS/Windows, see lpr.el and ps-print.el.
  253. (defcustom direct-print-region-use-command-dot-com t
  254. "If non-nil, use command.com to print on Windows 9x."
  255. :type 'boolean
  256. :group 'dos-fns
  257. :group 'w32)
  258. ;; Function to actually send data to the printer port.
  259. ;; Supports writing directly, and using various programs.
  260. (defun direct-print-region-helper (printer
  261. start end
  262. lpr-prog
  263. _delete-text _buf _display
  264. rest)
  265. (let* (;; Ignore case when matching known external program names.
  266. (case-fold-search t)
  267. ;; Convert / to \ in printer name, for sake of external programs.
  268. (printer
  269. (if (stringp printer)
  270. (subst-char-in-string ?/ ?\\ printer)
  271. printer))
  272. ;; Find a directory that is local, to work-around Windows bug.
  273. (safe-dir
  274. (let ((safe-dirs (list "c:/" (getenv "windir") (getenv "TMPDIR"))))
  275. (while (not (file-attributes (car safe-dirs)))
  276. (setq safe-dirs (cdr safe-dirs)))
  277. (car safe-dirs)))
  278. (tempfile
  279. (subst-char-in-string
  280. ?/ ?\\
  281. (make-temp-name
  282. (expand-file-name "EP" temporary-file-directory))))
  283. ;; capture output for diagnosis
  284. (errbuf (list (get-buffer-create " *print-region-helper*") t)))
  285. ;; It seems that we must be careful about the directory name that
  286. ;; gets added to the printer port name by write-region when using
  287. ;; the standard "PRN" or "LPTx" ports, because the write can fail if
  288. ;; the directory is on a network drive. The same is true when
  289. ;; asking command.com to copy the file.
  290. ;; No action is needed for UNC printer names, which is just as well
  291. ;; because `expand-file-name' doesn't support UNC names on MS-DOS.
  292. (if (and (stringp printer) (not (string-match "^\\\\" printer)))
  293. (setq printer
  294. (subst-char-in-string ?/ ?\\ (expand-file-name printer safe-dir))))
  295. ;; Handle known programs specially where necessary.
  296. (unwind-protect
  297. (cond
  298. ;; nprint.exe is the standard print command on Netware
  299. ((string-match "^nprint\\(\\.exe\\)?$" (file-name-nondirectory lpr-prog))
  300. (write-region start end tempfile nil 0)
  301. (call-process lpr-prog nil errbuf nil
  302. tempfile (concat "P=" printer)))
  303. ;; print.exe is a standard command on NT
  304. ((string-match "^print\\(\\.exe\\)?$" (file-name-nondirectory lpr-prog))
  305. ;; Be careful not to invoke print.exe on MS-DOS or Windows 9x
  306. ;; though, because it is a TSR program there (hangs Emacs).
  307. (or (and (eq system-type 'windows-nt)
  308. (null (getenv "winbootdir")))
  309. (error "Printing via print.exe is not supported on MS-DOS or Windows 9x"))
  310. ;; It seems that print.exe always appends a form-feed so we
  311. ;; should make sure to omit the last FF in the data.
  312. (if (and (> end start)
  313. (char-equal (char-before end) ?\C-l))
  314. (setq end (1- end)))
  315. ;; cancel out annotate function for non-PS case
  316. (let ((write-region-annotate-functions nil))
  317. (write-region start end tempfile nil 0))
  318. (call-process lpr-prog nil errbuf nil
  319. (concat "/D:" printer) tempfile))
  320. ;; support lpr and similar programs for convenience, but
  321. ;; supply an explicit filename because the NT version of lpr
  322. ;; can't read from stdin.
  323. ((> (length lpr-prog) 0)
  324. (write-region start end tempfile nil 0)
  325. (setq rest (append rest (list tempfile)))
  326. (apply 'call-process lpr-prog nil errbuf nil rest))
  327. ;; Run command.com to access printer port on Windows 9x, unless
  328. ;; we are supposed to append to an existing (non-empty) file,
  329. ;; to work around a bug in Windows 9x that prevents Win32
  330. ;; programs from accessing LPT ports reliably.
  331. ((and (eq system-type 'windows-nt)
  332. (getenv "winbootdir")
  333. ;; Allow cop-out so command.com isn't invoked
  334. direct-print-region-use-command-dot-com
  335. ;; file-attributes fails on LPT ports on Windows 9x but
  336. ;; not on NT, so handle both cases for safety.
  337. (eq (or (nth 7 (file-attributes printer)) 0) 0))
  338. (write-region start end tempfile nil 0)
  339. (let ((w32-quote-process-args nil))
  340. (call-process "command.com" nil errbuf nil "/c"
  341. (format "copy /b %s %s" tempfile printer))))
  342. ;; write directly to the printer port
  343. (t
  344. (write-region start end printer t 0)))
  345. ;; ensure we remove the tempfile if created
  346. (if (file-exists-p tempfile)
  347. (delete-file tempfile)))))
  348. (defvar printer-name)
  349. (declare-function default-printer-name "w32fns.c")
  350. (defun direct-print-region-function (start end
  351. &optional lpr-prog
  352. delete-text buf display
  353. &rest rest)
  354. "DOS/Windows-specific function to print the region on a printer.
  355. Writes the region to the device or file which is a value of
  356. `printer-name' \(which see\), unless the value of `lpr-command'
  357. indicates a specific program should be invoked."
  358. ;; DOS printers need the lines to end with CR-LF pairs, so make
  359. ;; sure it always happens that way, unless the buffer is binary.
  360. (let* ((coding coding-system-for-write)
  361. (coding-base
  362. (if (null coding) 'undecided (coding-system-base coding)))
  363. (eol-type (coding-system-eol-type coding-base))
  364. ;; Make each print-out eject the final page, but don't waste
  365. ;; paper if the file ends with a form-feed already.
  366. (write-region-annotate-functions
  367. (cons
  368. (lambda (_start end)
  369. (if (not (char-equal (char-before end) ?\C-l))
  370. `((,end . "\f"))))
  371. write-region-annotate-functions))
  372. (printer (or (and (boundp 'dos-printer)
  373. (stringp (symbol-value 'dos-printer))
  374. (symbol-value 'dos-printer))
  375. printer-name
  376. (default-printer-name))))
  377. (or (eq coding-system-for-write 'no-conversion)
  378. (setq coding-system-for-write
  379. (aref eol-type 1))) ; force conversion to DOS EOLs
  380. (direct-print-region-helper printer start end lpr-prog
  381. delete-text buf display rest)))
  382. (defvar print-region-function)
  383. (defvar lpr-headers-switches)
  384. (setq print-region-function 'direct-print-region-function)
  385. ;; Set this to nil if you have a port of the `pr' program
  386. ;; (e.g., from GNU Textutils), or if you have an `lpr'
  387. ;; program (see above) that can print page headers.
  388. ;; If `lpr-headers-switches' is non-nil (the default) and
  389. ;; `print-region-function' is set to `dos-print-region-function',
  390. ;; then requests to print page headers will be silently
  391. ;; ignored, and `print-buffer' and `print-region' produce
  392. ;; the same output as `lpr-buffer' and `lpr-region', accordingly.
  393. (setq lpr-headers-switches "(page headers are not supported)")
  394. (defvar ps-printer-name)
  395. (defun direct-ps-print-region-function (start end
  396. &optional lpr-prog
  397. delete-text buf display
  398. &rest rest)
  399. "DOS/Windows-specific function to print the region on a PostScript printer.
  400. Writes the region to the device or file which is a value of
  401. `ps-printer-name' \(which see\), unless the value of `ps-lpr-command'
  402. indicates a specific program should be invoked."
  403. (let ((printer (or (and (boundp 'dos-ps-printer)
  404. (stringp (symbol-value 'dos-ps-printer))
  405. (symbol-value 'dos-ps-printer))
  406. ps-printer-name
  407. (default-printer-name))))
  408. (direct-print-region-helper printer start end lpr-prog
  409. delete-text buf display rest)))
  410. (defvar ps-print-region-function)
  411. (setq ps-print-region-function 'direct-ps-print-region-function)
  412. ;(setq ps-lpr-command "gs")
  413. ;(setq ps-lpr-switches '("-q" "-dNOPAUSE" "-sDEVICE=epson" "-r240x60"
  414. ; "-sOutputFile=LPT1"))
  415. (provide 'dos-w32)
  416. ;;; dos-w32.el ends here