1:   // TarEntry.cs
2:   // Copyright (C) 2001 Mike Krueger
3:   //
4:   // This program is free software; you can redistribute it and/or
5:   // modify it under the terms of the GNU General Public License
6:   // as published by the Free Software Foundation; either version 2
7:   // of the License, or (at your option) any later version.
8:   //
9:   // This program is distributed in the hope that it will be useful,
10:   // but WITHOUT ANY WARRANTY; without even the implied warranty of
11:   // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12:   // GNU General Public License for more details.
13:   //
14:   // You should have received a copy of the GNU General Public License
15:   // along with this program; if not, write to the Free Software
16:   // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17:   //
18:   // Linking this library statically or dynamically with other modules is
19:   // making a combined work based on this library.  Thus, the terms and
20:   // conditions of the GNU General Public License cover the whole
21:   // combination.
22:   // 
23:   // As a special exception, the copyright holders of this library give you
24:   // permission to link this library with independent modules to produce an
25:   // executable, regardless of the license terms of these independent
26:   // modules, and to copy and distribute the resulting executable under
27:   // terms of your choice, provided that you also meet, for each linked
28:   // independent module, the terms and conditions of the license of that
29:   // module.  An independent module is a module which is not derived from
30:   // or based on this library.  If you modify this library, you may extend
31:   // this exception to your version of the library, but you are not
32:   // obligated to do so.  If you do not wish to do so, delete this
33:   // exception statement from your version.
34:  
35:   using System;
36:   using System.IO;
37:   using System.Text;
38:  
39:   namespace ICSharpCode.SharpZipLib.Tar {
40:       
41:       /// <summary>
42:       /// This class represents an entry in a Tar archive. It consists
43:       /// of the entry's header, as well as the entry's File. Entries
44:       /// can be instantiated in one of three ways, depending on how
45:       /// they are to be used.
46:       /// <p>
47:       /// TarEntries that are created from the header bytes read from
48:       /// an archive are instantiated with the TarEntry( byte[] )
49:       /// constructor. These entries will be used when extracting from
50:       /// or listing the contents of an archive. These entries have their
51:       /// header filled in using the header bytes. They also set the File
52:       /// to null, since they reference an archive entry not a file.</p>
53:       /// <p>
54:       /// TarEntries that are created from Files that are to be written
55:       /// into an archive are instantiated with the TarEntry( File )
56:       /// constructor. These entries have their header filled in using
57:       /// the File's information. They also keep a reference to the File
58:       /// for convenience when writing entries.</p>
59:       /// <p>
60:       /// Finally, TarEntries can be constructed from nothing but a name.
61:       /// This allows the programmer to construct the entry by hand, for
62:       /// instance when only an InputStream is available for writing to
63:       /// the archive, and the header information is constructed from
64:       /// other information. In this case the header fields are set to
65:       /// defaults and the File is set to null.</p>
66:       /// 
67:       /// <p>
68:       /// The C structure for a Tar Entry's header is:
69:       /// <pre>
70:       /// struct header {
71:       ///     char    name[NAMSIZ];
72:       ///     char    mode[8];
73:       ///     char    uid[8];
74:       ///     char    gid[8];
75:       ///     char    size[12];
76:       ///     char    mtime[12];
77:       ///     char    chksum[8];
78:       ///     char    linkflag;
79:       ///     char    linkname[NAMSIZ];
80:       ///     char    magic[8];
81:       ///     char    uname[TUNMLEN];
82:       ///     char    gname[TGNMLEN];
83:       ///     char    devmajor[8];
84:       ///     char    devminor[8];
85:       ///     } header;
86:       /// </pre>
87:       /// </p>
88:       /// <see cref="TarHeader"/>
89:       /// </summary>
90:       public class TarEntry
91:       {
92:           /// <summary>
93:           /// If this entry represents a File, this references it.
94:           /// </summary>
95:           protected string    file;
96:           
97:           /// <summary>
98:           /// This is the entry's header information.
99:           /// </summary>
100:           protected TarHeader    header;
101:           
102:           /// <summary>
103:           /// Only Create Entries with the static CreateXYZ methods or a headerBuffer.
104:           /// </summary>
105:           private TarEntry()
106:           {
107:           }
108:           
109:           /// <summary>
110:           /// Construct an entry from an archive's header bytes. File is set
111:           /// to null.
112:           /// </summary>
113:           /// <param name = "headerBuf">
114:           /// The header bytes from a tar archive entry.
115:           /// </param>
116:           public TarEntry(byte[] headerBuf)
117:           {
118:               this.Initialize();
119:               this.ParseTarHeader(this.headerheaderBuf);
120:           }
121:           
122:                   
123:           
124:           /// <summary>
125:           /// Construct an entry with only a name. This allows the programmer
126:           /// to construct the entry's header "by hand". File is set to null.
127:           /// </summary>
128:           public static TarEntry CreateTarEntry(string name)
129:           {
130:               TarEntry entry new TarEntry();
131:               entry.Initialize();
132:               entry.NameTarHeader(entry.headername);
133:               return entry;
134:           }
135:           
136:           /// <summary>
137:           /// Construct an entry for a file. File is set to file, and the
138:           /// header is constructed from information from the file.
139:           /// </summary>
140:           /// <param name = "fileName">
141:           /// The file that the entry represents.
142:           /// </param>
143:           public static TarEntry CreateEntryFromFile(string fileName)
144:           {
145:               TarEntry entry new TarEntry();
146:               entry.Initialize();
147:               entry.GetFileTarHeader(entry.headerfileName);
148:               return entry;
149:           }
150:           
151:           /// <summary>
152:           /// Initialization code common to all constructors.
153:           /// </summary>
154:           void Initialize()
155:           {
156:               this.file   null;
157:               this.header new TarHeader();
158:           }
159:           
160:           /// <summary>
161:           /// Determine if the two entries are equal. Equality is determined
162:           /// by the header names being equal.
163:           /// </summary>
164:           /// <returns>
165:           /// True if the entries are equal.
166:           /// </returns>
167:           public override bool Equals(object it)
168:           {
169:               if (!(it is TarEntry)) {
170:                   return false;
171:               }
172:               return this.header.name.ToString().Equals(((TarEntry)it).header.name.ToString());
173:           }
174:           
175:           /// <summary>
176:           /// Must be overridden when you override Equals.
177:           /// </summary>
178:           public override int GetHashCode()
179:           {
180:               return this.header.name.ToString().GetHashCode();
181:           }
182:           
183:           
184:           /// <summary>
185:           /// Determine if the given entry is a descendant of this entry.
186:           /// Descendancy is determined by the name of the descendant
187:           /// starting with this entry's name.
188:           /// </summary>
189:           /// <param name = "desc">
190:           /// Entry to be checked as a descendent of this.
191:           /// </param>
192:           /// <returns>
193:           /// True if entry is a descendant of this.
194:           /// </returns>
195:           public bool IsDescendent(TarEntry desc)
196:           {
197:               return desc.header.name.ToString().StartsWith(this.header.name.ToString());
198:           }
199:           
200:           /// <summary>
201:           /// Get this entry's header.
202:           /// </summary>
203:           /// <returns>
204:           /// This entry's TarHeader.
205:           /// </returns>
206:           public TarHeader TarHeader {
207:               get {
208:                   return this.header;
209:               }
210:           }
211:           
212:           /// <summary>
213:           /// Get/Set this entry's name.
214:           /// </summary>
215:           public string Name {
216:               get {
217:                   return this.header.name.ToString();
218:               }
219:               set {
220:                   this.header.name new StringBuilder(value);
221:               }
222:           }
223:           
224:           /// <summary>
225:           /// Get/set this entry's user id.
226:           /// </summary>
227:           public int UserId {
228:               get {
229:                   return this.header.userId;
230:               }
231:               set {
232:                   this.header.userId value;
233:               }
234:           }
235:           
236:           /// <summary>
237:           /// Get/set this entry's group id.
238:           /// </summary>
239:           public int GroupId {
240:               get {
241:                   return this.header.groupId;
242:               }
243:               set {
244:                   this.header.groupId value;
245:               }
246:           }
247:           
248:           /// <summary>
249:           /// Get/set this entry's user name.
250:           /// </summary>
251:           public string UserName {
252:               get {
253:                   return this.header.userName.ToString();
254:               }
255:               set {
256:                   this.header.userName new StringBuilder(value);
257:               }
258:           }
259:           
260:           /// <summary>
261:           /// Get/set this entry's group name.
262:           /// </summary>
263:           public string GroupName {
264:               get {
265:                   return this.header.groupName.ToString();
266:               }
267:               set {
268:                   this.header.groupName new StringBuilder(value);
269:               }
270:           }
271:           
272:           /// <summary>
273:           /// Convenience method to set this entry's group and user ids.
274:           /// </summary>
275:           /// <param name="userId">
276:           /// This entry's new user id.
277:           /// </param>
278:           /// <param name="groupId">
279:           /// This entry's new group id.
280:           /// </param>
281:           public void SetIds(int userIdint groupId)
282:           {
283:               UserId  userId
284:               GroupId groupId;
285:           }
286:           
287:           /// <summary>
288:           /// Convenience method to set this entry's group and user names.
289:           /// </summary>
290:           /// <param name="userName">
291:           /// This entry's new user name.
292:           /// </param>
293:           /// <param name="groupName">
294:           /// This entry's new group name.
295:           /// </param>
296:           public void SetNames(string userNamestring groupName)
297:           {
298:               UserName  userName;
299:               GroupName groupName;
300:           }
301:  
302:   //    TODO :
303:   //        /**
304:   //        * Set this entry's modification time. The parameter passed
305:   //        * to this method is in "Java time".
306:   //        *
307:   //        * @param time This entry's new modification time.
308:   //        */
309:   //        public void setModTime( long time )
310:   //        {
311:   //            this.header.modTime = time / 1000;
312:   //        }
313:           
314:           /// Convert time to DateTimes
315:           /**
316:           * Get/Set this entry's modification time.
317:           *
318:           * @param time This entry's new modification time.
319:           */
320:           public DateTime ModTime {
321:               get {
322:                   return this.header.modTime;
323:               }
324:               set {
325:                   this.header.modTime value;
326:               }
327:           }
328:           
329:           /// <summary>
330:           /// Get this entry's file.
331:           /// </summary>
332:           /// <returns>
333:           /// This entry's file.
334:           /// </returns>
335:           public string File {
336:               get {
337:                   return this.file;
338:               }
339:           }
340:           
341:           /// <summary>
342:           /// Get/set this entry's file size.
343:           /// </summary>
344:           public long Size {
345:               get {
346:                   return this.header.size;
347:               }
348:               set {
349:                   this.header.size value;
350:               }
351:           }
352:           
353:           /// <summary>
354:           /// Convenience method that will modify an entry's name directly
355:           /// in place in an entry header buffer byte array.
356:           /// </summary>
357:           /// <param name="outbuf">
358:           /// The buffer containing the entry header to modify.
359:           /// </param>
360:           /// <param name="newName">
361:           /// The new name to place into the header buffer.
362:           /// </param>
363:           public void AdjustEntryName(byte[] outbufstring newName)
364:           {
365:               int offset 0;
366:               offset TarHeader.GetNameBytes(new StringBuilder(newName), outbufoffsetTarHeader.NAMELEN);
367:           }
368:           
369:           /// <summary>
370:           /// Return whether or not this entry represents a directory.
371:           /// </summary>
372:           /// <returns>
373:           /// True if this entry is a directory.
374:           /// </returns>
375:           public bool IsDirectory
376:           {
377:               get {
378:                   if (this.file != null) {
379:                       return Directory.Exists(file);
380:                   }
381:                   
382:                   if (this.header != null) {
383:                       if (this.header.linkFlag == TarHeader.LF_DIR || this.header.name.ToString().EndsWith"/" )) {
384:                           return true;
385:                       }
386:                   }
387:                   return false;
388:               }
389:           }
390:           
391:           /// <summary>
392:           /// Fill in a TarHeader with information from a File.
393:           /// </summary>
394:           /// <param name="hdr">
395:           /// The TarHeader to fill in.
396:           /// </param>
397:           /// <param name="file">
398:           /// The file from which to get the header information.
399:           /// </param>
400:           public void GetFileTarHeader(TarHeader hdrstring file)
401:           {
402:               this.file file;
403:               
404:               string name Path.GetDirectoryName(file);
405:               
406:               if (Path.DirectorySeparatorChar == '\\') { // check if the OS is a windows
407:                   // Strip off drive letters!
408:                   if (name.Length 2) {
409:                       char ch1 name[0];
410:                       char ch2 name[1];
411:                       
412:                       if (ch2 == ':' && Char.IsLetter(ch1)) {
413:                           name name.Substring(2);
414:                       }
415:                   }
416:               }
417:               
418:               name name.Replace(Path.DirectorySeparatorChar'/');
419:               
420: