comparison src/dired-msw.c @ 771:943eaba38521

[xemacs-hg @ 2002-03-13 08:51:24 by ben] The big ben-mule-21-5 check-in! Various files were added and deleted. See CHANGES-ben-mule. There are still some test suite failures. No crashes, though. Many of the failures have to do with problems in the test suite itself rather than in the actual code. I'll be addressing these in the next day or so -- none of the test suite failures are at all critical. Meanwhile I'll be trying to address the biggest issues -- i.e. build or run failures, which will almost certainly happen on various platforms. All comments should be sent to ben@xemacs.org -- use a Cc: if necessary when sending to mailing lists. There will be pre- and post- tags, something like pre-ben-mule-21-5-merge-in, and post-ben-mule-21-5-merge-in.
author ben
date Wed, 13 Mar 2002 08:54:06 +0000
parents 11502791fc1c
children a5954632b187
comparison
equal deleted inserted replaced
770:336a418893b5 771:943eaba38521
1 /* fast dired replacement routines for mswindows. 1 /* fast dired replacement routines for mswindows.
2 Copyright (C) 1998 Darryl Okahata 2 Copyright (C) 1998 Darryl Okahata
3 Portions Copyright (C) 1992, 1994 by Sebastian Kremer <sk@thp.uni-koeln.de> 3 Portions Copyright (C) 1992, 1994 by Sebastian Kremer <sk@thp.uni-koeln.de>
4 Copyright (C) 2000, 2001 Ben Wing.
4 5
5 This file is part of XEmacs. 6 This file is part of XEmacs.
6 7
7 XEmacs is free software; you can redistribute it and/or modify it 8 XEmacs is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the 9 under the terms of the GNU General Public License as published by the
18 along with XEmacs; see the file COPYING. If not, write to 19 along with XEmacs; see the file COPYING. If not, write to
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 20 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA. */ 21 Boston, MA 02111-1307, USA. */
21 22
22 /* Synched up with: Not in FSF. */ 23 /* Synched up with: Not in FSF. */
24
23 25
24 /* 26 /*
25 * Parts of this code (& comments) were taken from ls-lisp.el 27 * Parts of this code (& comments) were taken from ls-lisp.el
26 * Author: Sebastian Kremer <sk@thp.uni-koeln.de> 28 * Author: Sebastian Kremer <sk@thp.uni-koeln.de>
27 */ 29 */
62 64
63 /* 65 /*
64 * Set INDENT_LISTING to non-zero if the inserted text should be shifted 66 * Set INDENT_LISTING to non-zero if the inserted text should be shifted
65 * over by two spaces. 67 * over by two spaces.
66 */ 68 */
67 #define INDENT_LISTING 0 69 #define INDENT_LISTING 0
68 70
69 #define ROUND_FILE_SIZES 4096 71 #define ROUND_FILE_SIZES 4096
70 72
71 73
72 #include <config.h> 74 #include <config.h>
73 #include "lisp.h" 75 #include "lisp.h"
74 76
75 #include "buffer.h" 77 #include "buffer.h"
76 #include "nt.h"
77 #include "regex.h" 78 #include "regex.h"
78 79
79 #include "sysdir.h" 80 #include "sysdir.h"
80 #include "sysproc.h"
81 #include "sysfile.h" 81 #include "sysfile.h"
82 #include "sysfloat.h" 82 #include "sysfloat.h"
83 83 #include "sysproc.h"
84 #include "syspwd.h"
85 #include "systime.h"
86 #include "syswindows.h"
84 87
85 static int mswindows_ls_sort_case_insensitive; 88 static int mswindows_ls_sort_case_insensitive;
86 static Fixnum mswindows_ls_round_file_size; 89 static Fixnum mswindows_ls_round_file_size;
87 90
88 Lisp_Object Qmswindows_insert_directory; 91 Lisp_Object Qmswindows_insert_directory;
89 92 Lisp_Object Qwildcard_to_regexp;
90 extern Lisp_Object Vmswindows_downcase_file_names; /* in device-msw.c */ 93
94 extern Lisp_Object Vmswindows_downcase_file_names; /* in device-msw.c */
91 95
92 enum mswindows_sortby 96 enum mswindows_sortby
93 { 97 {
94 MSWINDOWS_SORT_BY_NAME, 98 MSWINDOWS_SORT_BY_NAME,
95 MSWINDOWS_SORT_BY_NAME_NOCASE, 99 MSWINDOWS_SORT_BY_NAME_NOCASE,
96 MSWINDOWS_SORT_BY_MOD_DATE, 100 MSWINDOWS_SORT_BY_MOD_DATE,
97 MSWINDOWS_SORT_BY_SIZE 101 MSWINDOWS_SORT_BY_SIZE
98 }; 102 };
99 103
100 104
101 static enum mswindows_sortby mswindows_sort_method; 105 static enum mswindows_sortby mswindows_sort_method;
102 static int mswindows_reverse_sort; 106 static int mswindows_reverse_sort;
107
108 /* We create our own structure because the cFileName field in
109 WIN32_FIND_DATA is in external format and of fixed size, which we
110 may exceed when translating. */
111
112 typedef struct
113 {
114 DWORD dwFileAttributes;
115 FILETIME ftCreationTime;
116 FILETIME ftLastAccessTime;
117 FILETIME ftLastWriteTime;
118 DWORD nFileSizeHigh;
119 DWORD nFileSizeLow;
120 Intbyte *cFileName;
121 } Win32_file;
122
123 typedef struct
124 {
125 Dynarr_declare (Win32_file);
126 } Win32_file_dynarr;
127
103 128
104 129
105 #define CMPDWORDS(t1a, t1b, t2a, t2b) \ 130 #define CMPDWORDS(t1a, t1b, t2a, t2b) \
106 (((t1a) == (t2a)) ? (((t1b) == (t2b)) ? 0 : (((t1b) < (t2b)) ? -1 : 1)) \ 131 (((t1a) == (t2a)) ? (((t1b) == (t2b)) ? 0 : (((t1b) < (t2b)) ? -1 : 1)) \
107 : (((t1a) < (t2a)) ? -1 : 1)) 132 : (((t1a) < (t2a)) ? -1 : 1))
108 133
109 134
110 static int 135 static int
111 mswindows_ls_sort_fcn (const void *elem1, const void *elem2) 136 mswindows_ls_sort_fcn (const void *elem1, const void *elem2)
112 { 137 {
113 WIN32_FIND_DATA *e1, *e2; 138 Win32_file *e1, *e2;
114 int status; 139 int status;
115 140
116 e1 = *(WIN32_FIND_DATA **)elem1; 141 e1 = (Win32_file *) elem1;
117 e2 = *(WIN32_FIND_DATA **)elem2; 142 e2 = (Win32_file *) elem2;
143
118 switch (mswindows_sort_method) 144 switch (mswindows_sort_method)
119 { 145 {
120 case MSWINDOWS_SORT_BY_NAME: 146 case MSWINDOWS_SORT_BY_NAME:
121 status = strcmp(e1->cFileName, e2->cFileName); 147 status = strcmp (e1->cFileName, e2->cFileName);
122 break; 148 break;
123 case MSWINDOWS_SORT_BY_NAME_NOCASE: 149 case MSWINDOWS_SORT_BY_NAME_NOCASE:
124 status = _stricmp(e1->cFileName, e2->cFileName); 150 status = qxestrcasecmp (e1->cFileName, e2->cFileName);
125 break; 151 break;
126 case MSWINDOWS_SORT_BY_MOD_DATE: 152 case MSWINDOWS_SORT_BY_MOD_DATE:
127 status = CMPDWORDS(e1->ftLastWriteTime.dwHighDateTime, 153 status = CMPDWORDS (e1->ftLastWriteTime.dwHighDateTime,
128 e1->ftLastWriteTime.dwLowDateTime, 154 e1->ftLastWriteTime.dwLowDateTime,
129 e2->ftLastWriteTime.dwHighDateTime, 155 e2->ftLastWriteTime.dwHighDateTime,
130 e2->ftLastWriteTime.dwLowDateTime); 156 e2->ftLastWriteTime.dwLowDateTime);
131 break; 157 break;
132 case MSWINDOWS_SORT_BY_SIZE: 158 case MSWINDOWS_SORT_BY_SIZE:
133 status = CMPDWORDS(e1->nFileSizeHigh, e1->nFileSizeLow, 159 status = CMPDWORDS (e1->nFileSizeHigh, e1->nFileSizeLow,
134 e2->nFileSizeHigh, e2->nFileSizeLow); 160 e2->nFileSizeHigh, e2->nFileSizeLow);
135 break; 161 break;
136 default: 162 default:
137 status = 0; 163 status = 0;
138 break; 164 break;
139 } 165 }
142 status = -status; 168 status = -status;
143 } 169 }
144 return (status); 170 return (status);
145 } 171 }
146 172
147
148 static void 173 static void
149 mswindows_sort_files (WIN32_FIND_DATA **files, int nfiles, 174 mswindows_sort_files (Win32_file_dynarr *files,
150 enum mswindows_sortby sort_by, int reverse) 175 enum mswindows_sortby sort_by, int reverse)
151 { 176 {
152 mswindows_sort_method = sort_by; 177 mswindows_sort_method = sort_by;
153 mswindows_reverse_sort = reverse; 178 mswindows_reverse_sort = reverse;
154 qsort(files, nfiles, sizeof(WIN32_FIND_DATA *), mswindows_ls_sort_fcn); 179 qsort (Dynarr_atp (files, 0), Dynarr_length (files),
180 sizeof (Win32_file), mswindows_ls_sort_fcn);
155 } 181 }
156 182
157 183 static Win32_file_dynarr *
158 static WIN32_FIND_DATA * 184 mswindows_get_files (Lisp_Object dirfile, int nowild, Lisp_Object pattern,
159 mswindows_get_files (char *dirfile, int nowild, Lisp_Object pattern, 185 int hide_dot, int hide_system)
160 int hide_dot, int hide_system, int *nfiles) 186 {
161 { 187 Win32_file_dynarr *files = Dynarr_new (Win32_file);
162 WIN32_FIND_DATA *files; 188 struct re_pattern_buffer *bufp = NULL;
163 int array_size; 189 int findex;
164 struct re_pattern_buffer *bufp = NULL; 190 DECLARE_EISTRING (win32pattern);
165 int findex, len; 191 HANDLE fh;
166 char win32pattern[MAXNAMLEN+3]; 192
167 HANDLE fh;
168
169 /*
170 * Much of the following code and comments were taken from dired.c.
171 * Yes, this is something of a waste, but we want speed, speed, SPEED.
172 */
173 files = NULL;
174 array_size = *nfiles = 0;
175 while (1) 193 while (1)
176 { 194 {
177 if (!NILP(pattern)) 195 if (!NILP (pattern))
178 { 196 {
179 /* PATTERN might be a flawed regular expression. Rather than 197 /* PATTERN might be a flawed regular expression. Rather than
180 catching and signalling our own errors, we just call 198 catching and signalling our own errors, we just call
181 compile_pattern to do the work for us. */ 199 compile_pattern to do the work for us. */
182 bufp = compile_pattern (pattern, 0, Qnil, 0, ERROR_ME); 200 bufp = compile_pattern (pattern, 0, Qnil, 0, ERROR_ME);
183 } 201 }
184 /* Now *bufp is the compiled form of PATTERN; don't call anything 202 /* Now *bufp is the compiled form of PATTERN; don't call anything
185 which might compile a new regexp until we're done with the loop! */ 203 which might compile a new regexp until we're done with the loop! */
186 204
187 /* Initialize file info array */
188 array_size = 100; /* initial size */
189 files = xmalloc(array_size * sizeof (WIN32_FIND_DATA));
190
191 /* for Win32, we need to insure that the pathname ends with "\*". */ 205 /* for Win32, we need to insure that the pathname ends with "\*". */
192 strcpy (win32pattern, dirfile); 206 eicpy_lstr (win32pattern, dirfile);
193 if (!nowild) 207 if (!nowild)
194 { 208 {
195 len = strlen (win32pattern) - 1; 209 Charcount len = eicharlen (win32pattern) - 1;
196 if (!IS_DIRECTORY_SEP (win32pattern[len])) 210 if (!IS_DIRECTORY_SEP (eigetch_char (win32pattern, len)))
197 strcat (win32pattern, "\\"); 211 eicat_c (win32pattern, "\\");
198 strcat (win32pattern, "*"); 212 eicat_c (win32pattern, "*");
199 } 213 }
214 eito_external (win32pattern, Qmswindows_tstr);
200 215
201 /* 216 /*
202 * Here, we use FindFirstFile()/FindNextFile() instead of opendir(), 217 * Here, we use FindFirstFile()/FindNextFile() instead of opendir(),
203 * xemacs_stat(), & friends, because xemacs_stat() is VERY expensive in 218 * qxe_stat(), & friends, because qxe_stat() is VERY expensive in
204 * terms of time. Hence, we take the time to write complicated 219 * terms of time. Hence, we take the time to write complicated
205 * Win32-specific code, instead of simple Unix-style stuff. 220 * Win32-specific code, instead of simple Unix-style stuff.
206 */ 221 */
207 findex = 0; 222 findex = 0;
208 fh = INVALID_HANDLE_VALUE; 223 fh = INVALID_HANDLE_VALUE;
209 224
210 while (1) 225 while (1)
211 { 226 {
212 int len; 227 Bytecount len;
213 char *filename; 228 DECLARE_EISTRING (filename);
214 int result; 229 int result;
230 WIN32_FIND_DATAW finddat;
231 Win32_file file;
215 232
216 if (fh == INVALID_HANDLE_VALUE) 233 if (fh == INVALID_HANDLE_VALUE)
217 { 234 {
218 fh = FindFirstFile(win32pattern, &files[findex]); 235 fh = qxeFindFirstFile (eiextdata (win32pattern), &finddat);
219 if (fh == INVALID_HANDLE_VALUE) 236 if (fh == INVALID_HANDLE_VALUE)
220 { 237 report_file_error ("Opening directory", dirfile);
221 report_file_error ("Opening directory",
222 build_string (dirfile));
223 }
224 } 238 }
225 else 239 else
226 { 240 {
227 if (!FindNextFile(fh, &files[findex])) 241 if (! qxeFindNextFile (fh, &finddat))
228 { 242 {
229 if (GetLastError() == ERROR_NO_MORE_FILES) 243 if (GetLastError () == ERROR_NO_MORE_FILES)
230 { 244 break;
231 break; 245 FindClose (fh);
232 } 246 report_file_error ("Reading directory", dirfile);
233 FindClose(fh);
234 report_file_error ("Reading directory",
235 build_string (dirfile));
236 } 247 }
237 } 248 }
238 249
239 filename = files[findex].cFileName; 250 file.dwFileAttributes = finddat.dwFileAttributes;
240 if (!NILP(Vmswindows_downcase_file_names)) 251 file.ftCreationTime = finddat.ftCreationTime;
241 { 252 file.ftLastAccessTime = finddat.ftLastAccessTime;
242 strlwr(filename); 253 file.ftLastWriteTime = finddat.ftLastWriteTime;
243 } 254 file.nFileSizeHigh = finddat.nFileSizeHigh;
244 len = strlen(filename); 255 file.nFileSizeLow = finddat.nFileSizeLow;
245 result = (NILP(pattern) 256 eicpy_ext (filename, (Extbyte *) finddat.cFileName,
246 || (0 <= re_search (bufp, filename, 257 Qmswindows_tstr);
258
259 if (!NILP (Vmswindows_downcase_file_names))
260 eilwr (filename);
261 len = eilen (filename);
262 result = (NILP (pattern)
263 || (0 <= re_search (bufp, eidata (filename),
247 len, 0, len, 0))); 264 len, 0, len, 0)));
248 if (result) 265 if (result)
249 { 266 {
250 if ( ! (filename[0] == '.' && 267 if ( ! (eigetch_char (filename, 0) == '.' &&
251 ((hide_system && (filename[1] == '\0' || 268 ((hide_system &&
252 (filename[1] == '.' && 269 (eigetch_char (filename, 1) == '\0' ||
253 filename[2] == '\0'))) || 270 (eigetch_char (filename, 1) == '.' &&
271 eigetch_char (filename, 2) == '\0'))) ||
254 hide_dot))) 272 hide_dot)))
255 { 273 {
256 if (++findex >= array_size) 274 file.cFileName =
257 { 275 (Intbyte *) xmalloc (sizeof (Intbyte) * (1 + len));
258 array_size = findex * 2; 276 memcpy (file.cFileName, eidata (filename), len);
259 files = xrealloc(files, 277 file.cFileName[len] = '\0';
260 array_size * sizeof(WIN32_FIND_DATA)); 278 Dynarr_add (files, file);
261 }
262 } 279 }
263 } 280 }
264 } 281 }
265 if (fh != INVALID_HANDLE_VALUE) 282 if (fh != INVALID_HANDLE_VALUE)
266 { 283 FindClose (fh);
267 FindClose (fh);
268 }
269 *nfiles = findex;
270 break; 284 break;
271 } 285 }
272 return (files); 286 return (files);
273 } 287 }
274 288
275 289 static Lisp_Object
276 static void 290 mswindows_format_file (Win32_file *file, int display_size, int add_newline)
277 mswindows_format_file (WIN32_FIND_DATA *file, char *buf, int display_size, 291 {
278 int add_newline) 292 Lisp_Object luser;
279 { 293 double file_size;
280 char *cptr; 294 DECLARE_EISTRING (puta);
281 int len; 295 CIntbyte buf[666];
282 Lisp_Object luser; 296
283 double file_size;
284
285 len = strlen(file->cFileName);
286 file_size = 297 file_size =
287 file->nFileSizeHigh * (double)UINT_MAX + file->nFileSizeLow; 298 file->nFileSizeHigh * (double)UINT_MAX + file->nFileSizeLow;
288 cptr = buf;
289 #if INDENT_LISTING 299 #if INDENT_LISTING
290 *cptr++ = ' '; 300 eicat_c (puta, " ");
291 *cptr++ = ' ';
292 #endif 301 #endif
293 if (display_size) 302 if (display_size)
294 { 303 {
295 sprintf(cptr, "%6d ", (int)((file_size + 1023.) / 1024.)); 304 sprintf (buf, "%6d ", (int)((file_size + 1023.) / 1024.));
296 cptr += 7; 305 eicat_c (puta, buf);
297 } 306 }
298 if (file->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 307 if (file->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
299 { 308 eicat_c (puta, "d");
300 *cptr++ = 'd'; 309 else
301 } else { 310 eicat_c (puta, "-");
302 *cptr++ = '-'; 311 buf[0] = buf[3] = buf[6] = 'r';
303 }
304 cptr[0] = cptr[3] = cptr[6] = 'r';
305 if (file->dwFileAttributes & FILE_ATTRIBUTE_READONLY) 312 if (file->dwFileAttributes & FILE_ATTRIBUTE_READONLY)
306 { 313 buf[1] = buf[4] = buf[7] = '-';
307 cptr[1] = cptr[4] = cptr[7] = '-'; 314 else
308 } else { 315 buf[1] = buf[4] = buf[7] = 'w';
309 cptr[1] = cptr[4] = cptr[7] = 'w'; 316 {
310 } 317 int is_executable = 0;
311 if ((file->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) || 318
312 (len > 4 && 319 if (file->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
313 (_stricmp(&file->cFileName[len - 4], ".exe") == 0 320 is_executable = 1;
314 || _stricmp(&file->cFileName[len - 4], ".com") == 0 321 else if (qxestrcharlen (file->cFileName) > 4)
315 || _stricmp(&file->cFileName[len - 4], ".bat") == 0 322 {
323 Intbyte *end = file->cFileName + qxestrlen (file->cFileName);
324 DEC_CHARPTR (end);
325 DEC_CHARPTR (end);
326 DEC_CHARPTR (end);
327 DEC_CHARPTR (end);
328 if (qxestrcasecmp (end, ".exe") == 0
329 || qxestrcasecmp (end, ".com") == 0
330 || qxestrcasecmp (end, ".bat") == 0
316 #if 0 331 #if 0
317 || _stricmp(&file->cFileName[len - 4], ".pif") == 0 332 || qxestrcasecmp (end, ".pif") == 0
318 #endif 333 #endif
319 ))) 334 )
320 { 335 is_executable = 1;
321 cptr[2] = cptr[5] = cptr[8] = 'x'; 336 }
322 } else { 337 if (is_executable)
323 cptr[2] = cptr[5] = cptr[8] = '-'; 338 buf[2] = buf[5] = buf[8] = 'x';
324 } 339 else
325 cptr += 9; 340 buf[2] = buf[5] = buf[8] = '-';
341 }
342 buf[9] = '\0';
343 eicat_c (puta, buf);
326 if (file->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 344 if (file->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
327 { 345 eicat_c (puta, " 2 ");
328 strcpy(cptr, " 2 "); 346 else
329 } else { 347 eicat_c (puta, " 1 ");
330 strcpy(cptr, " 1 "); 348 luser = Fuser_login_name (Qnil);
331 } 349 if (!STRINGP (luser))
332 cptr += 5; 350 sprintf (buf, "%-9d", 0);
333 luser = Fuser_login_name(Qnil); 351 else
334 if (!STRINGP(luser)) 352 {
335 { 353 Intbyte *str;
336 sprintf(cptr, "%-9d", 0); 354
337 } else { 355 str = XSTRING_DATA (luser);
338 char *str; 356 sprintf (buf, "%-8s ", str);
339 357 }
340 str = XSTRING_DATA(luser); 358 eicat_raw (puta, (Intbyte *) buf, strlen (buf));
341 sprintf(cptr, "%-8s ", str); 359 {
342 } 360 CIntbyte *cptr = buf;
343 while (*cptr) 361 sprintf (buf, "%-8d ", getgid ());
344 { 362 cptr += 9;
363 if (file_size > 99999999.0)
364 {
365 file_size = (file_size + 1023.0) / 1024.;
366 if (file_size > 999999.0)
367 sprintf (cptr, "%6.0fMB ", (file_size + 1023.0) / 1024.);
368 else
369 sprintf (cptr, "%6.0fKB ", file_size);
370 }
371 else
372 sprintf (cptr, "%8.0f ", file_size);
373 while (*cptr)
345 ++cptr; 374 ++cptr;
346 } 375 {
347 sprintf(cptr, "%-8d ", getgid()); 376 time_t t, now;
348 cptr += 9; 377 Intbyte *ctimebuf;
349 if (file_size > 99999999.0) 378
350 { 379 if (
351 file_size = (file_size + 1023.0) / 1024.; 380 #if 0
352 if (file_size > 999999.0) 381 /*
382 * This doesn't work.
383 * This code should be correct ...
384 */
385 FileTimeToLocalFileTime (&file->ftLastWriteTime, &localtime) &&
386 ((t = mswindows_convert_time (localtime)) != 0) &&
387 #else
388 /*
389 * But this code "works" ...
390 */
391 ((t = mswindows_convert_time (file->ftLastWriteTime)) != 0) &&
392 #endif
393 ((ctimebuf = qxe_ctime (&t)) != NULL))
353 { 394 {
354 sprintf(cptr, "%6.0fMB ", (file_size + 1023.0) / 1024.); 395 memcpy (cptr, &ctimebuf[4], 7);
355 } else { 396 now = time (NULL);
356 sprintf(cptr, "%6.0fKB ", file_size); 397 if (now - t > (365. / 2.0) * 86400.)
398 {
399 /* more than 6 months */
400 cptr[7] = ' ';
401 memcpy (&cptr[8], &ctimebuf[20], 4);
402 }
403 else
404 {
405 /* less than 6 months */
406 memcpy (&cptr[7], &ctimebuf[11], 5);
407 }
408 cptr += 12;
409 *cptr++ = ' ';
410 *cptr++ = '\0';
357 } 411 }
358 } else { 412 }
359 sprintf(cptr, "%8.0f ", file_size);
360 }
361 while (*cptr)
362 {
363 ++cptr;
364 }
365 {
366 time_t t, now;
367 char *ctimebuf;
368 extern char *sys_ctime(const time_t *t); /* in nt.c */
369
370 if (
371 #if 0
372 /*
373 * This doesn't work.
374 * This code should be correct ...
375 */
376 FileTimeToLocalFileTime(&file->ftLastWriteTime, &localtime) &&
377 ((t = convert_time(localtime)) != 0) &&
378 #else
379 /*
380 * But this code "works" ...
381 */
382 ((t = convert_time(file->ftLastWriteTime)) != 0) &&
383 #endif
384 ((ctimebuf = sys_ctime(&t)) != NULL))
385 {
386 memcpy(cptr, &ctimebuf[4], 7);
387 now = time(NULL);
388 if (now - t > (365. / 2.0) * 86400.)
389 {
390 /* more than 6 months */
391 cptr[7] = ' ';
392 memcpy(&cptr[8], &ctimebuf[20], 4);
393 } else {
394 /* less than 6 months */
395 memcpy(&cptr[7], &ctimebuf[11], 5);
396 }
397 cptr += 12;
398 *cptr++ = ' ';
399 }
400 } 413 }
414
415 eicat_c (puta, buf);
416 eicat_raw (puta, file->cFileName, qxestrlen (file->cFileName));
401 if (add_newline) 417 if (add_newline)
402 { 418 eicat_c (puta, "\n");
403 sprintf(cptr, "%s\n", file->cFileName); 419
404 } 420 return eimake_string (puta);
405 else
406 {
407 strcpy(cptr, file->cFileName);
408 }
409 } 421 }
410 422
411 423
412 DEFUN ("mswindows-insert-directory", Fmswindows_insert_directory, 2, 4, 0, /* 424 DEFUN ("mswindows-insert-directory", Fmswindows_insert_directory, 2, 4, 0, /*
413 Insert directory listing for FILE, formatted according to SWITCHES. 425 Insert directory listing for FILE, formatted according to SWITCHES.
417 Optional fourth arg FULL-DIRECTORY-P means file is a directory and 429 Optional fourth arg FULL-DIRECTORY-P means file is a directory and
418 switches do not contain `d', so that a full listing is expected. 430 switches do not contain `d', so that a full listing is expected.
419 */ 431 */
420 (file, switches, wildcard, full_directory_p)) 432 (file, switches, wildcard, full_directory_p))
421 { 433 {
422 Lisp_Object result, handler, wildpat, fns, basename; 434 Lisp_Object handler, wildpat = Qnil, basename = Qnil;
423 char *switchstr; 435 int nfiles = 0, i;
424 int nfiles, i; 436 int hide_system = 1, hide_dot = 1, reverse = 0, display_size = 0;
425 int hide_system, hide_dot, reverse, display_size; 437 Win32_file_dynarr *files;
426 WIN32_FIND_DATA *files, **sorted_files; 438 enum mswindows_sortby sort_by =
427 enum mswindows_sortby sort_by; 439 (mswindows_ls_sort_case_insensitive ? MSWINDOWS_SORT_BY_NAME_NOCASE
428 char fmtbuf[MAXNAMLEN+100]; /* larger than necessary */ 440 : MSWINDOWS_SORT_BY_NAME);
429 struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5; 441 struct gcpro gcpro1, gcpro2, gcpro3;
430 442
431 result = Qnil; 443 GCPRO3 (file, wildpat, basename);
432 wildpat = Qnil; 444
433 fns = Qnil; 445 CHECK_STRING (file);
434 basename = Qnil; 446 if (!NILP (wildpat))
435 GCPRO5(result, file, wildpat, fns, basename); 447 CHECK_STRING (wildpat);
436 sorted_files = NULL; 448
437 switchstr = NULL; 449 handler = Ffind_file_name_handler (file, Qmswindows_insert_directory);
438 hide_system = 1; 450 if (!NILP (handler))
439 hide_dot = 1; 451 {
440 display_size = 0; 452 UNGCPRO;
441 reverse = 0; 453 return call5 (handler, Qmswindows_insert_directory, file, switches,
442 sort_by = (mswindows_ls_sort_case_insensitive 454 wildcard, full_directory_p);
443 ? MSWINDOWS_SORT_BY_NAME_NOCASE 455 }
444 : MSWINDOWS_SORT_BY_NAME); 456
445 nfiles = 0; 457 if (!NILP (switches))
446 while (1) 458 {
447 { 459 Intbyte *cptr, *cptr_end;
448 handler = Ffind_file_name_handler (file, Qmswindows_insert_directory); 460
449 if (!NILP(handler)) 461 CHECK_STRING (switches);
462 cptr = XSTRING_DATA (switches);
463 cptr_end = cptr + XSTRING_LENGTH (switches);
464 while (cptr < cptr_end)
450 { 465 {
451 result = call5(handler, Qmswindows_insert_directory, file, switches, 466 Emchar ch = charptr_emchar (cptr);
452 wildcard, full_directory_p); 467 switch (ch)
453 break;
454 }
455 CHECK_STRING (file);
456 if (!NILP(switches))
457 {
458 char *cptr;
459
460 CHECK_STRING (switches);
461 switchstr = XSTRING_DATA(switches);
462 for (cptr = switchstr; *cptr; ++cptr)
463 { 468 {
464 switch (*cptr) 469 case 'A':
465 { 470 hide_dot = 0;
466 case 'A': 471 break;
467 hide_dot = 0; 472 case 'a':
468 break; 473 hide_system = 0;
469 case 'a': 474 hide_dot = 0;
470 hide_system = 0; 475 break;
471 hide_dot = 0; 476 case 'r':
472 break; 477 reverse = 1;
473 case 'r': 478 break;
474 reverse = 1; 479 case 's':
475 break; 480 display_size = 1;
476 case 's': 481 break;
477 display_size = 1; 482 case 'S':
478 break; 483 sort_by = MSWINDOWS_SORT_BY_SIZE;
479 case 'S': 484 break;
480 sort_by = MSWINDOWS_SORT_BY_SIZE; 485 case 't':
481 break; 486 sort_by = MSWINDOWS_SORT_BY_MOD_DATE;
482 case 't':
483 sort_by = MSWINDOWS_SORT_BY_MOD_DATE;
484 break;
485 }
486 }
487 }
488
489 if (!NILP(wildcard))
490 {
491 Lisp_Object newfile;
492
493 file = Fdirectory_file_name (file);
494 basename = Ffile_name_nondirectory(file);
495 fns = intern("wildcard-to-regexp");
496 wildpat = call1(fns, basename);
497 newfile = Ffile_name_directory(file);
498 if (NILP(newfile))
499 {
500 /* Ffile_name_directory() can GC */
501 newfile = Ffile_name_directory(Fexpand_file_name(file, Qnil));
502 }
503 file = newfile;
504 }
505 if (!NILP(wildcard) || !NILP(full_directory_p))
506 {
507 CHECK_STRING(file);
508 if (!NILP(wildpat))
509 {
510 CHECK_STRING(wildpat);
511 }
512
513 files = mswindows_get_files(XSTRING_DATA(file), FALSE, wildpat,
514 hide_dot, hide_system, &nfiles);
515 if (files == NULL || nfiles == 0)
516 {
517 break; 487 break;
518 } 488 }
489 INC_CHARPTR (cptr);
519 } 490 }
520 else 491 }
492
493 if (!NILP (wildcard))
494 {
495 Lisp_Object newfile;
496
497 file = Fdirectory_file_name (file);
498 basename = Ffile_name_nondirectory (file);
499 wildpat = call1 (Qwildcard_to_regexp, basename);
500 newfile = Ffile_name_directory (file);
501 if (NILP (newfile))
502 newfile = Ffile_name_directory (Fexpand_file_name (file, Qnil));
503 file = newfile;
504 }
505
506 files = mswindows_get_files (file,
507 NILP (wildcard) && NILP (full_directory_p),
508 wildpat, hide_dot, hide_system);
509
510 if (Dynarr_length (files) > 1)
511 mswindows_sort_files (files, sort_by, reverse);
512 if (!NILP (wildcard) || !NILP (full_directory_p))
513 {
514 /*
515 * By using doubles, we can handle files up to 2^53 bytes in
516 * size (IEEE doubles have 53 bits of resolution). However,
517 * as we divide by 1024 (or 2^10), the total size is
518 * accurate up to 2^(53+10) --> 2^63 bytes.
519 *
520 * Hopefully, we won't have to handle these file sizes anytime
521 * soon.
522 */
523 double total_size, file_size, block_size;
524
525 if ((block_size = mswindows_ls_round_file_size) <= 0)
521 { 526 {
522 files = mswindows_get_files(XSTRING_DATA(file), TRUE, wildpat, 527 block_size = 0;
523 hide_dot, hide_system, &nfiles);
524 } 528 }
525 if ((sorted_files = xmalloc(nfiles * sizeof(WIN32_FIND_DATA *))) 529 total_size = 0;
526 == NULL) 530 for (i = 0; i < Dynarr_length (files); ++i)
527 { 531 {
528 break; 532 Win32_file *file = Dynarr_atp (files, i);
533 file_size =
534 file->nFileSizeHigh * (double)UINT_MAX +
535 file->nFileSizeLow;
536 if (block_size > 0)
537 {
538 /*
539 * Round file_size up to the next nearest block size.
540 */
541 file_size =
542 floor ((file_size + block_size - 1) / block_size)
543 * block_size;
544 }
545 /* Here, we round to the nearest 1K */
546 total_size += floor ((file_size + 512.) / 1024.);
529 } 547 }
530 for (i = 0; i < nfiles; ++i) 548 {
531 { 549 Intbyte tempbuf[666];
532 sorted_files[i] = &files[i]; 550
533 } 551 qxesprintf (tempbuf,
534 if (nfiles > 1)
535 {
536 mswindows_sort_files(sorted_files, nfiles, sort_by, reverse);
537 }
538 if (!NILP(wildcard) || !NILP(full_directory_p))
539 {
540 /*
541 * By using doubles, we can handle files up to 2^53 bytes in
542 * size (IEEE doubles have 53 bits of resolution). However,
543 * as we divide by 1024 (or 2^10), the total size is
544 * accurate up to 2^(53+10) --> 2^63 bytes.
545 *
546 * Hopefully, we won't have to handle these file sizes anytime
547 * soon.
548 */
549 double total_size, file_size, block_size;
550
551 if ((block_size = mswindows_ls_round_file_size) <= 0)
552 {
553 block_size = 0;
554 }
555 total_size = 0;
556 for (i = 0; i < nfiles; ++i)
557 {
558 file_size =
559 sorted_files[i]->nFileSizeHigh * (double)UINT_MAX +
560 sorted_files[i]->nFileSizeLow;
561 if (block_size > 0)
562 {
563 /*
564 * Round file_size up to the next nearest block size.
565 */
566 file_size =
567 floor((file_size + block_size - 1) / block_size)
568 * block_size;
569 }
570 /* Here, we round to the nearest 1K */
571 total_size += floor((file_size + 512.) / 1024.);
572 }
573 sprintf(fmtbuf,
574 #if INDENT_LISTING 552 #if INDENT_LISTING
575 /* ANSI C compilers auto-concatenate adjacent strings */ 553 /* ANSI C compilers auto-concatenate adjacent strings */
576 " " 554 " "
577 #endif 555 #endif
578 "total %.0f\n", total_size); 556 "total %.0f\n", total_size);
579 buffer_insert1(current_buffer, build_string(fmtbuf)); 557 buffer_insert1 (current_buffer, build_intstring (tempbuf));
580 } 558 }
581 for (i = 0; i < nfiles; ++i) 559 }
582 { 560 for (i = 0; i < Dynarr_length (files); ++i)
583 mswindows_format_file(sorted_files[i], fmtbuf, display_size, TRUE); 561 {
584 buffer_insert1(current_buffer, build_string(fmtbuf)); 562 struct gcpro ngcpro1;
585 } 563 Lisp_Object fmtfile =
586 break; 564 mswindows_format_file (Dynarr_atp (files, i), display_size, TRUE);
587 } 565 NGCPRO1 (fmtfile);
588 if (sorted_files) 566 buffer_insert1 (current_buffer, fmtfile);
589 { 567 NUNGCPRO;
590 xfree(sorted_files); 568 }
591 } 569 for (i = 0; i < Dynarr_length (files); ++i)
570 {
571 Win32_file *file = Dynarr_atp (files, i);
572 xfree (file->cFileName);
573 }
574 Dynarr_free (files);
575
592 UNGCPRO; 576 UNGCPRO;
593 return (result); 577 return Qnil;
594 } 578 }
595 579
596 580
597 581
598 /************************************************************************/ 582 /************************************************************************/
601 585
602 void 586 void
603 syms_of_dired_mswindows (void) 587 syms_of_dired_mswindows (void)
604 { 588 {
605 DEFSYMBOL (Qmswindows_insert_directory); 589 DEFSYMBOL (Qmswindows_insert_directory);
590 DEFSYMBOL (Qwildcard_to_regexp);
606 591
607 DEFSUBR (Fmswindows_insert_directory); 592 DEFSUBR (Fmswindows_insert_directory);
608 } 593 }
609 594
610 595
611 void 596 void
612 vars_of_dired_mswindows (void) 597 vars_of_dired_mswindows (void)
613 { 598 {
614 DEFVAR_BOOL ("mswindows-ls-sort-case-insensitive", &mswindows_ls_sort_case_insensitive /* 599 DEFVAR_BOOL ("mswindows-ls-sort-case-insensitive",
600 &mswindows_ls_sort_case_insensitive /*
615 *Non-nil means filenames are sorted in a case-insensitive fashion. 601 *Non-nil means filenames are sorted in a case-insensitive fashion.
616 Nil means filenames are sorted in a case-sensitive fashion, just like Unix. 602 Nil means filenames are sorted in a case-sensitive fashion, just like Unix.
617 */ ); 603 */ );
618 mswindows_ls_sort_case_insensitive = 1; 604 mswindows_ls_sort_case_insensitive = 1;
619 605