Index: client/libs/ar/writer.py |
diff --git a/client/libs/ar/writer.py b/client/libs/ar/writer.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..19c10f6b1c0dee778f0efdb666783ad4a1178103 |
--- /dev/null |
+++ b/client/libs/ar/writer.py |
@@ -0,0 +1,109 @@ |
+# Copyright 2016 The LUCI Authors. All rights reserved. |
+# Use of this source code is governed under the Apache License, Version 2.0 |
+# that can be found in the LICENSE file. |
+ |
+import shutil |
+ |
+ |
+class ArWriter(object): |
+ """Write an ar archive to the given output buffer.""" |
+ |
+ # Mode sentinels |
+ MODE_HEADER = [] |
+ MODE_CONTENTS = [] |
+ |
+ def __init__(self, obuf): |
+ self.obuf = obuf |
+ self.obuf.write("!<arch>\n") |
+ self.mode = ArWriter.MODE_HEADER |
+ self.needspadding = False |
+ self.bytesrequired = 0 |
+ |
+ def header(self, fp, size, modtime, ownerid, groupid, filemod): |
+ """Write a file information to the archive.""" |
+ assert self.mode == ArWriter.MODE_HEADER |
+ assert self.bytesrequired == 0 |
+ |
+ # File name, 16 bytes |
+ self.obuf.write("#1/%-13s" % str(len(fp))) |
M-A Ruel
2016/06/09 21:50:44
single quotes everywhere
mithro
2016/06/14 12:15:55
Done.
|
+ # Modtime, 12 bytes |
+ self.obuf.write("%-12i" % modtime) |
+ # Owner ID, 6 bytes |
+ self.obuf.write("%-6i" % ownerid) |
+ # Group ID, 6 bytes |
+ self.obuf.write("%-6i" % groupid) |
+ # File mode, 8 bytes |
+ self.obuf.write("%-8o" % filemod) |
+ |
+ datasize = size+len(fp) |
+ # File size, 10 bytes |
+ self.obuf.write("%-10s" % datasize) |
+ # File magic, 2 bytes |
+ self.obuf.write("\x60\n") |
+ |
+ # Filename - BSD variant |
+ self.obuf.write(fp) |
+ |
+ self.mode = ArWriter.MODE_CONTENTS |
+ self.bytesrequired = size |
+ self.needspadding = datasize % 2 != 0 |
+ |
+ def _write(self, ibuf=None, s=None): |
+ assert self.mode == ArWriter.MODE_CONTENTS |
+ assert ibuf is not None or s is not None |
+ |
+ if ibuf is not None: |
+ start = ibuf.tell() |
+ shutil.copyfileobj(ibuf, self.obuf) |
+ end = ibuf.tell() |
+ self.bytesrequired -= end-start |
+ |
+ if s is not None: |
+ self.obuf.write(s) |
+ self.bytesrequired -= len(s) |
+ |
+ if self.bytesrequired == 0: |
+ if self.needspadding: |
+ self.obuf.write("\n") |
+ self.mode = ArWriter.MODE_HEADER |
+ |
+ def write(self, ibuf_or_str): |
+ """Write the file body to the archive.""" |
+ try: |
+ self._write(ibuf=ibuf_or_str) |
+ except AttributeError: |
M-A Ruel
2016/06/09 21:50:44
?
mithro
2016/06/14 12:15:55
Rather than trying to check if ibuf_or_str will wo
|
+ self._write(s=ibuf_or_str) |
+ |
+ def close(self): |
+ """Close the archive. Will close the output buffer.""" |
+ assert self.bytesrequired == 0 |
+ assert self.mode == ArWriter.MODE_HEADER |
+ self.obuf.close() |
+ |
+ |
+class ArDefaultWriter(ArWriter): |
+ """Write an ar archive using defaults to the given output buffer. |
+ |
+ Only a file's name and content are needed to create the archive, all of the |
+ modification time, user, group and mode information will be set to default |
+ values. This means that you don't need to perform an expensive stat the file. |
+ """ |
+ DEFAULT_MODTIME = 1447140471 |
+ DEFAULT_USER = 1000 |
+ DEFAULT_GROUP = 1000 |
+ DEFAULT_MODE = 0100640 # 100640 -- Octal |
+ |
+ def header(self, name, size, |
+ modtime=None, ownerid=None, groupid=None, filemod=None): |
+ assert modtime is None |
+ assert ownerid is None |
+ assert groupid is None |
+ assert filemod is None |
+ ArWriter.header( |
+ self, name, size, |
+ self.DEFAULT_MODTIME, self.DEFAULT_USER, self.DEFAULT_GROUP, |
+ self.DEFAULT_MODE) |
+ |
+ def add(self, name, data): |
+ self.header(name, len(data)) |
+ self._write(s=data) |