Chromium Code Reviews| 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..bd411b02c2385231d3f7f12317ee80b6ee3d8afb |
| --- /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 = [] |
|
M-A Ruel
2016/06/14 13:26:17
= object()
mithro
2016/06/16 11:37:11
Obsolete.
|
| + MODE_CONTENTS = [] |
| + |
| + def __init__(self, obuf): |
| + self.obuf = obuf |
|
M-A Ruel
2016/06/14 13:26:17
I'd prefer self._obuf and _ prefix for others too
mithro
2016/06/16 11:37:11
Obsolete.
|
| + 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/14 13:26:17
Can you use struct.pack() like you did with unpack
mithro
2016/06/16 11:37:11
No stuct.pack doesn't do this style of output (lef
|
| + # 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): |
|
M-A Ruel
2016/06/14 13:26:17
I think I'd prefer two functions. This is confusin
mithro
2016/06/16 11:37:11
Obsolete.
|
| + 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: |
|
M-A Ruel
2016/06/14 13:26:17
if not self._bytesrequired:
mithro
2016/06/16 11:37:11
Obsolete.
|
| + if self.needspadding: |
| + self.obuf.write('\n') |
| + self.mode = ArWriter.MODE_HEADER |
| + |
| + def write(self, ibuf_or_str): |
|
M-A Ruel
2016/06/14 13:26:17
I'd prefer a writestr() function and not try to gu
mithro
2016/06/16 11:37:11
Obsolete.
|
| + """Write the file body to the archive.""" |
| + try: |
| + self._write(ibuf=ibuf_or_str) |
| + except AttributeError: |
| + 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): |
|
M-A Ruel
2016/06/14 13:26:17
I'd prefer to not expose these arguments at all.
mithro
2016/06/16 11:37:11
Obsolete.
|
| + 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) |