;+ ; NAME: ; do_file ; PURPOSE: ; File manipulations: moving, copying and deleting files ; CALLING SEQUENCE: FUNCTION do_file, file, dest, copy=copy, delete=delete, move=move, $ silent=silent, buffer=buffer ; INPUTS: ; file scalar: type: string ; file spec (may contain wildcards) ; dest scalar: type: string ; destination for copy or move ; OPTIONAL INPUT PARAMETERS: ; /copy copies file ; /delete deletes file ; /move moves (renames) file ; silent=silent controls messages to screen ; OUTPUTS: ; Result 1 = success ; 0 = failure ; on Windows and Unix no status code is available ; and the value 1 is returned. ; INCLUDE: @compile_opt.pro ; On error, return to caller ; CALLS: ; SetFileSpec, GetFileSpec, hide_env ; RESTRICTIONS: ; One and only one keyword can be set ; PROCEDURE: ; > FIND_FILE is used to locate a file matching the input `file' value. ; (if file='' or absent no action is taken). ; > `dest' is checked by DO_PARSE to make sure it is a valid file name ; ; On VMS all file operations use call_external to execute system functions or ; other F77 functions: ; /move calls the system function LIB$RENAME_FILE first (if not succesful ; the F77 function COPY_FILE is called followed by a LIB$DELETE_FILE ; call (this should work across drives) ; /copy calls the F77 function COPY_FILE ; /delete calls the system function LIB$DELETE_FILE ; ; > On Windows and Unix the proper shell command is spawned, with 'file' and ; 'destin' as arguments. ; RESTRICTIONS: ; If large groups of files are processes the concatenated list of file names ; can create a cp,rm or mv command that is too long. The threshold for the length ; of a shell command apparently is 128kB (the actual limit is somewhat lower in ; practice, and apparently is not the same for all Linux boxes on the subnet, ; even those running the same Linux distribution). ; Currently files are processed in groups that keep the length below 124 kB. So far ; this appears to be a safe value. Use the keyword buffer to override the default. ; MODIFICATION HISTORY: ; JAN-1995, Paul Hick (UCSD/CASS) ; APR-2000, Paul Hick (UCSD/CASS) ; IDL 5.4 has some added feature: /hide keyword for Windows; exit status ; for Windows and unix. The unix modifications haven't been tested since ; we don't have 5.4 on any of the Linux boxes yet. ; JAN-2002, Paul Hick (UCSD/CASS) ; Fixed a pretty lethal bug in the file delete procedure on Windows. ; (it might try to delete all files in the current directory!!). ; SEP-2005, Paul Hick (UCSD/CASS) ; Added code to copy multiple files at once. ; JUL-2007, Paul Hick (UCSD/CASS; pphick@ucsd.edu) ; Replaced findfile by file_search ;- InitVar, copy , /key InitVar, delete, /key InitVar, move , /key InitVar, silent, 0 InitVar, buffer, 124L ;124.85 nfile = n_elements(file) icmd = [move, copy, delete] usage_message = total(icmd) NE 1 OR nfile EQ 0 CASE delete OF 0: BEGIN usage_message = usage_message OR IsType(dest, /undefined) IF NOT usage_message THEN usage_message = dest[0] EQ '' OR n_elements(dest) GT 1 END 1: usage_message = usage_message OR IsType(dest,/defined) ENDCASE IF usage_message THEN BEGIN message, /info, 'Usage: status = do_file( file1 [, file2], /copy, /move, /delete])' RETURN, 0 ENDIF CASE delete OF 0: BEGIN dest = dest[0] SetFileSpec, dest, /parse, status=status IF NOT status THEN BEGIN message, /info, 'invalid destination: '+hide_env(dest) RETURN, 0 ENDIF END 1: dest = '' ; For delete no destination is allowed ENDCASE icmd = (where(icmd))[0] CASE !version.os_family OF 'vms' : cmd_spawn = (['move','copy','del' ])[icmd] ; Used in 'case of' block 'Windows': cmd_spawn = (['move','copy','del' ])[icmd] ; DOS commands 'unix' : cmd_spawn = (['mv' ,'cp' ,'rm -f'])[icmd] ; Shell commands ENDCASE CASE nfile GT 1 OF 0: BEGIN file = file[0] IF file EQ '' OR (file_search(file))[0] EQ '' THEN BEGIN message, /info, 'file not found for '+cmd_spawn+': '+hide_env(file) RETURN, 0 ENDIF hide_file = hide_env(file, silent=silent) hide_dest = hide_env(dest, silent=silent) IF silent LE 0 THEN $ message, /info, cmd_spawn+' '+hide_file+([' '+hide_dest,' '+hide_dest,''])[icmd] ; Don't put quotes around 'file' if it contains a wildcard quote = (['','"'])[strpos(file,'*') EQ -1] xfile = quote+file+quote ; Don't put quotes around 'dest' if it is the null string quote = (['','"'])[strlen(dest) NE 0] xdest = quote+dest+quote CASE !version.os_family OF 'vms': BEGIN CASE cmd_spawn OF 'move': BEGIN status = call_external('LIBRTL','lib$rename_file',file,dest) ; Works on same drive only?? IF NOT status THEN BEGIN status = call_external('EXEIDL','copy_file',file,dest) ; Should work across drives IF status THEN status = call_external('LIBRTL','lib$delete_file',file) ENDIF END 'copy': status = call_external('EXEIDL','copy_file',file,dest) 'del' : status = call_external('LIBRTL','lib$delete_file',file) ENDCASE END 'Windows': BEGIN ; IDL 5.4 introduced some enhancements to the spawn command: ; /hide keyword to minimized command window ; exit_status keyword to return status of spawned command ; If destination is the null string (for delete) than attaching it to ; the command string is pretty lethal. The delete command would read ; del "file name" "" ; The trailing set of quotes is interpreted as "*", i.e. would result ; in deleting all files in the current directory. (The double quotes ; were originally put in to deal with file names containing blanks.) IF !version.release LT 5.4 THEN BEGIN spawn, cmd_spawn+' '+xfile+' '+xdest status = 1B ENDIF ELSE BEGIN spawn, cmd_spawn+' '+xfile+' '+xdest, /hide, exit_status=status status = status EQ 0 ; Assume that 0 indicates success ENDELSE END 'unix': BEGIN IF !version.release LT 5.4 THEN BEGIN spawn, cmd_spawn+' '+xfile+' '+xdest status = 1B ENDIF ELSE BEGIN spawn, cmd_spawn+' '+xfile+' '+xdest, exit_status=status status = status EQ 0 ENDELSE END ENDCASE IF NOT status THEN $ message, /info, cmd_spawn+' '+hide_file+([' '+hide_dest,' '+hide_dest,''])[icmd]+': error' END 1: BEGIN ;IF silent LE 0 THEN message, /info, strcompress(nfile,/rem)+' files' CASE !version.os_family OF 'vms' : message, 'multiple files not implemented; probably never will' 'Windows': message, 'multiple files not implemented; and, no I will not' 'unix' : BEGIN ; On linux there is a 128 kB limit on the length of a spawned command. ; This becomes a problem when dealing with lots of files. ; Here the array of files is processed in chunks that hopefully don't ; overflow the buffer. IF strmid(cmd_spawn,0,2) NE 'rm' THEN $ cmd_spawn = cmd_spawn+' --target="'+dest+'"' SetFileSpec, file ; If all the input file specs include a directory and it is the same directory ; for all files we drop the directory, and prefix the spawned command with ; a cd to the source directory. This allows us to squeeze more files on the ; command line. srce_dir = GetFileSpec(upto='directory') use_cd = srce_dir[0] NE '' AND (where(srce_dir[1:*] NE srce_dir[0]))[0] EQ -1 CASE use_cd OF 0: cmd_prefix = '' 1: BEGIN srce_dir = srce_dir[0] file = GetFileSpec(from='name') cmd_prefix = 'cd "'+srce_dir+'"; ' END ENDCASE ; 128*1024 = 131072; 128257 works; 128288 too long; 125.25*1024=128256 max_len = max( strlen(file) )+3 ; Each file will be put in quotes with a preceeding blank cmd_len = floor(buffer*1024L)-strlen(cmd_prefix+cmd_spawn) nmax = cmd_len/max_len ; # files per command ngrp = nfile/nmax ; # groups of files IF nfile GT ngrp*nmax THEN ngrp = ngrp+1 status = 1 FOR i=0,ngrp-1 DO BEGIN ; Process files in ngrp groups of nmax files cmd = file[i*nmax:(i+1)*nmax-1 < (nfile-1)] n = n_elements(cmd) cmd = cmd_prefix+cmd_spawn+' "'+strjoin(cmd,'" "')+'"' IF silent LE 0 THEN $ message, /info, cmd_spawn+' ('+strcompress(n,/rem)+' files;'+ $ strcompress(strlen(cmd)/1024.0)+' kB)' CASE !version.release LT 5.4 OF 0: BEGIN spawn, cmd, exit_status=sts status = status AND sts EQ 0 END 1: spawn, cmd ENDCASE ENDFOR IF use_cd THEN file = filepath(root=srce_dir, file) END ENDCASE END ENDCASE RETURN, status & END