You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					225 lines
				
				4.9 KiB
			
		
		
			
		
	
	
					225 lines
				
				4.9 KiB
			| 
											3 years ago
										 | 'use strict' | ||
|  | 
 | ||
|  | const EE = require('events').EventEmitter | ||
|  | const cons = require('constants') | ||
|  | const fs = require('fs') | ||
|  | 
 | ||
|  | module.exports = (f, options, cb) => { | ||
|  |   if (typeof options === 'function') | ||
|  |     cb = options, options = {} | ||
|  | 
 | ||
|  |   const p = new Promise((res, rej) => { | ||
|  |     new Touch(validOpts(options, f, null)) | ||
|  |       .on('done', res).on('error', rej) | ||
|  |   }) | ||
|  | 
 | ||
|  |   return cb ? p.then(res => cb(null, res), cb) : p | ||
|  | } | ||
|  | 
 | ||
|  | module.exports.sync = module.exports.touchSync = (f, options) => | ||
|  |   (new TouchSync(validOpts(options, f, null)), undefined) | ||
|  | 
 | ||
|  | module.exports.ftouch = (fd, options, cb) => { | ||
|  |   if (typeof options === 'function') | ||
|  |     cb = options, options = {} | ||
|  | 
 | ||
|  |   const p = new Promise((res, rej) => { | ||
|  |     new Touch(validOpts(options, null, fd)) | ||
|  |       .on('done', res).on('error', rej) | ||
|  |   }) | ||
|  | 
 | ||
|  |   return cb ? p.then(res => cb(null, res), cb) : p | ||
|  | } | ||
|  | 
 | ||
|  | module.exports.ftouchSync = (fd, opt) => | ||
|  |   (new TouchSync(validOpts(opt, null, fd)), undefined) | ||
|  | 
 | ||
|  | const validOpts = (options, path, fd) => { | ||
|  |   options = Object.create(options || {}) | ||
|  |   options.fd = fd | ||
|  |   options.path = path | ||
|  | 
 | ||
|  |   // {mtime: true}, {ctime: true}
 | ||
|  |   // If set to something else, then treat as epoch ms value
 | ||
|  |   const now = parseInt(new Date(options.time || Date.now()).getTime() / 1000) | ||
|  |   if (!options.atime && !options.mtime) | ||
|  |     options.atime = options.mtime = now | ||
|  |   else { | ||
|  |     if (true === options.atime) | ||
|  |       options.atime = now | ||
|  | 
 | ||
|  |     if (true === options.mtime) | ||
|  |       options.mtime = now | ||
|  |   } | ||
|  | 
 | ||
|  |   let oflags = 0 | ||
|  |   if (!options.force) | ||
|  |     oflags = oflags | cons.O_RDWR | ||
|  | 
 | ||
|  |   if (!options.nocreate) | ||
|  |     oflags = oflags | cons.O_CREAT | ||
|  | 
 | ||
|  |   options.oflags = oflags | ||
|  |   return options | ||
|  | } | ||
|  | 
 | ||
|  | class Touch extends EE { | ||
|  |   constructor (options) { | ||
|  |     super(options) | ||
|  |     this.fd = options.fd | ||
|  |     this.path = options.path | ||
|  |     this.atime = options.atime | ||
|  |     this.mtime = options.mtime | ||
|  |     this.ref = options.ref | ||
|  |     this.nocreate = !!options.nocreate | ||
|  |     this.force = !!options.force | ||
|  |     this.closeAfter = options.closeAfter | ||
|  |     this.oflags = options.oflags | ||
|  |     this.options = options | ||
|  | 
 | ||
|  |     if (typeof this.fd !== 'number') { | ||
|  |       this.closeAfter = true | ||
|  |       this.open() | ||
|  |     } else | ||
|  |       this.onopen(null, this.fd) | ||
|  |   } | ||
|  | 
 | ||
|  |   emit (ev, data) { | ||
|  |     // we only emit when either done or erroring
 | ||
|  |     // in both cases, need to close
 | ||
|  |     this.close() | ||
|  |     return super.emit(ev, data) | ||
|  |   } | ||
|  | 
 | ||
|  |   close () { | ||
|  |     if (typeof this.fd === 'number' && this.closeAfter) | ||
|  |       fs.close(this.fd, () => {}) | ||
|  |   } | ||
|  | 
 | ||
|  |   open () { | ||
|  |     fs.open(this.path, this.oflags, (er, fd) => this.onopen(er, fd)) | ||
|  |   } | ||
|  | 
 | ||
|  |   onopen (er, fd) { | ||
|  |     if (er) { | ||
|  |       if (er.code === 'EISDIR') | ||
|  |         this.onopen(null, null) | ||
|  |       else if (er.code === 'ENOENT' && this.nocreate) | ||
|  |         this.emit('done') | ||
|  |       else | ||
|  |         this.emit('error', er) | ||
|  |     } else { | ||
|  |       this.fd = fd | ||
|  |       if (this.ref) | ||
|  |         this.statref() | ||
|  |       else if (!this.atime || !this.mtime) | ||
|  |         this.fstat() | ||
|  |       else | ||
|  |         this.futimes() | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   statref () { | ||
|  |     fs.stat(this.ref, (er, st) => { | ||
|  |       if (er) | ||
|  |         this.emit('error', er) | ||
|  |       else | ||
|  |         this.onstatref(st) | ||
|  |     }) | ||
|  |   } | ||
|  | 
 | ||
|  |   onstatref (st) { | ||
|  |     this.atime = this.atime && parseInt(st.atime.getTime()/1000, 10) | ||
|  |     this.mtime = this.mtime && parseInt(st.mtime.getTime()/1000, 10) | ||
|  |     if (!this.atime || !this.mtime) | ||
|  |       this.fstat() | ||
|  |     else | ||
|  |       this.futimes() | ||
|  |   } | ||
|  | 
 | ||
|  |   fstat () { | ||
|  |     const stat = this.fd ? 'fstat' : 'stat' | ||
|  |     const target = this.fd || this.path | ||
|  |     fs[stat](target, (er, st) => { | ||
|  |       if (er) | ||
|  |         this.emit('error', er) | ||
|  |       else | ||
|  |         this.onfstat(st) | ||
|  |     }) | ||
|  |   } | ||
|  | 
 | ||
|  |   onfstat (st) { | ||
|  |     if (typeof this.atime !== 'number') | ||
|  |       this.atime = parseInt(st.atime.getTime()/1000, 10) | ||
|  | 
 | ||
|  |     if (typeof this.mtime !== 'number') | ||
|  |       this.mtime = parseInt(st.mtime.getTime()/1000, 10) | ||
|  | 
 | ||
|  |     this.futimes() | ||
|  |   } | ||
|  | 
 | ||
|  |   futimes () { | ||
|  |     const utimes = this.fd ? 'futimes' : 'utimes' | ||
|  |     const target = this.fd || this.path | ||
|  |     fs[utimes](target, ''+this.atime, ''+this.mtime, er => { | ||
|  |       if (er) | ||
|  |         this.emit('error', er) | ||
|  |       else | ||
|  |         this.emit('done') | ||
|  |     }) | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | class TouchSync extends Touch { | ||
|  |   open () { | ||
|  |     try { | ||
|  |       this.onopen(null, fs.openSync(this.path, this.oflags)) | ||
|  |     } catch (er) { | ||
|  |       this.onopen(er) | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   statref () { | ||
|  |     let threw = true | ||
|  |     try { | ||
|  |       this.onstatref(fs.statSync(this.ref)) | ||
|  |       threw = false | ||
|  |     } finally { | ||
|  |       if (threw) | ||
|  |         this.close() | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   fstat () { | ||
|  |     let threw = true | ||
|  |     const stat = this.fd ? 'fstatSync' : 'statSync' | ||
|  |     const target = this.fd || this.path | ||
|  |     try { | ||
|  |       this.onfstat(fs[stat](target)) | ||
|  |       threw = false | ||
|  |     } finally { | ||
|  |       if (threw) | ||
|  |         this.close() | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   futimes () { | ||
|  |     let threw = true | ||
|  |     const utimes = this.fd ? 'futimesSync' : 'utimesSync' | ||
|  |     const target = this.fd || this.path | ||
|  |     try { | ||
|  |       fs[utimes](target, this.atime, this.mtime) | ||
|  |       threw = false | ||
|  |     } finally { | ||
|  |       if (threw) | ||
|  |         this.close() | ||
|  |     } | ||
|  |     this.emit('done') | ||
|  |   } | ||
|  | 
 | ||
|  |   close () { | ||
|  |     if (typeof this.fd === 'number' && this.closeAfter) | ||
|  |       try { fs.closeSync(this.fd) } catch (er) {} | ||
|  |   } | ||
|  | } |