1f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)/* udis86 - libudis86/syn.c
2f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) *
3f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) * Copyright (c) 2002-2013 Vivek Thampi
4f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) * All rights reserved.
5f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) *
6f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) * Redistribution and use in source and binary forms, with or without modification,
7f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) * are permitted provided that the following conditions are met:
8f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) *
9f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) *     * Redistributions of source code must retain the above copyright notice,
10f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) *       this list of conditions and the following disclaimer.
11f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) *     * Redistributions in binary form must reproduce the above copyright notice,
12f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) *       this list of conditions and the following disclaimer in the documentation
13f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) *       and/or other materials provided with the distribution.
14f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) *
15f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
19f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
22f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) */
26f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "types.h"
27f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "decode.h"
28f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "syn.h"
29f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "udint.h"
30f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
31f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)/* -----------------------------------------------------------------------------
32f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) * Intel Register Table - Order Matters (types.h)!
33f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) * -----------------------------------------------------------------------------
34f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) */
35f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)const char* ud_reg_tab[] =
36f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles){
37f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  "al",   "cl",   "dl",   "bl",
38f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  "ah",   "ch",   "dh",   "bh",
39f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  "spl",  "bpl",    "sil",    "dil",
40f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  "r8b",  "r9b",    "r10b",   "r11b",
41f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  "r12b", "r13b",   "r14b",   "r15b",
42f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
43f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  "ax",   "cx",   "dx",   "bx",
44f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  "sp",   "bp",   "si",   "di",
45f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  "r8w",  "r9w",    "r10w",   "r11w",
46f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  "r12w", "r13w"  , "r14w",   "r15w",
47f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
48f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  "eax",  "ecx",    "edx",    "ebx",
49f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  "esp",  "ebp",    "esi",    "edi",
50f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  "r8d",  "r9d",    "r10d",   "r11d",
51f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  "r12d", "r13d",   "r14d",   "r15d",
52f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
53f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  "rax",  "rcx",    "rdx",    "rbx",
54f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  "rsp",  "rbp",    "rsi",    "rdi",
55f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  "r8",   "r9",   "r10",    "r11",
56f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  "r12",  "r13",    "r14",    "r15",
57f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
58f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  "es",   "cs",   "ss",   "ds",
59f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  "fs",   "gs",
60f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
61f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  "cr0",  "cr1",    "cr2",    "cr3",
62f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  "cr4",  "cr5",    "cr6",    "cr7",
63f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  "cr8",  "cr9",    "cr10",   "cr11",
64f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  "cr12", "cr13",   "cr14",   "cr15",
65f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
66f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  "dr0",  "dr1",    "dr2",    "dr3",
67f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  "dr4",  "dr5",    "dr6",    "dr7",
68f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  "dr8",  "dr9",    "dr10",   "dr11",
69f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  "dr12", "dr13",   "dr14",   "dr15",
70f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
71f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  "mm0",  "mm1",    "mm2",    "mm3",
72f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  "mm4",  "mm5",    "mm6",    "mm7",
73f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
74f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  "st0",  "st1",    "st2",    "st3",
75f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  "st4",  "st5",    "st6",    "st7",
76f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
77f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  "xmm0", "xmm1",   "xmm2",   "xmm3",
78f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  "xmm4", "xmm5",   "xmm6",   "xmm7",
79f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  "xmm8", "xmm9",   "xmm10",  "xmm11",
80f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  "xmm12",  "xmm13",  "xmm14",  "xmm15",
81f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
82f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  "rip"
83f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)};
84f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
85f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
86f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)uint64_t
87f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)ud_syn_rel_target(struct ud *u, struct ud_operand *opr)
88f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles){
89f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  const uint64_t trunc_mask = 0xffffffffffffffffull >> (64 - u->opr_mode);
90f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  switch (opr->size) {
91f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  case 8 : return (u->pc + opr->lval.sbyte)  & trunc_mask;
92f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  case 16: return (u->pc + opr->lval.sword)  & trunc_mask;
93f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  case 32: return (u->pc + opr->lval.sdword) & trunc_mask;
94f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  default: UD_ASSERT(!"invalid relative offset size.");
95f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  }
96f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
97f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
98f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
99f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)/*
100f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) * asmprintf
101f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) *    Printf style function for printing translated assembly
102f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) *    output. Returns the number of characters written and
103f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) *    moves the buffer pointer forward. On an overflow,
104f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) *    returns a negative number and truncates the output.
105f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) */
106f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)int
107f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)ud_asmprintf(struct ud *u, char *fmt, ...)
108f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles){
109f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  int ret;
110f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  int avail;
111f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  va_list ap;
112f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  va_start(ap, fmt);
113f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  avail = u->asm_buf_size - u->asm_buf_fill - 1 /* nullchar */;
114f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  ret = vsnprintf((char*) u->asm_buf + u->asm_buf_fill, avail, fmt, ap);
115f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (ret < 0 || ret > avail) {
116f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      u->asm_buf_fill = u->asm_buf_size - 1;
117f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  } else {
118f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      u->asm_buf_fill += ret;
119f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  }
120f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  va_end(ap);
121f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return ret;
122f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
123f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
124f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
125f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void
126f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)ud_syn_print_addr(struct ud *u, uint64_t addr)
127f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles){
128f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  const char *name = NULL;
129f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (u->sym_resolver) {
130f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    int64_t offset = 0;
131f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    name = u->sym_resolver(u, addr, &offset);
132f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    if (name) {
133f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      if (offset) {
134f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        ud_asmprintf(u, "%s%+" FMT64 "d", name, offset);
135f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      } else {
136f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        ud_asmprintf(u, "%s", name);
137f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      }
138f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      return;
139f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    }
140f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  }
141f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  ud_asmprintf(u, "0x%" FMT64 "x", addr);
142f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
143f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
144f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
145f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void
146f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)ud_syn_print_imm(struct ud* u, const struct ud_operand *op)
147f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles){
148f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  uint64_t v;
149f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (op->_oprcode == OP_sI && op->size != u->opr_mode) {
150f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    if (op->size == 8) {
151f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      v = (int64_t)op->lval.sbyte;
152f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    } else {
153f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      UD_ASSERT(op->size == 32);
154f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      v = (int64_t)op->lval.sdword;
155f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    }
156f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    if (u->opr_mode < 64) {
157f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      v = v & ((1ull << u->opr_mode) - 1ull);
158f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    }
159f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  } else {
160f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    switch (op->size) {
161f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    case 8 : v = op->lval.ubyte;  break;
162f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    case 16: v = op->lval.uword;  break;
163f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    case 32: v = op->lval.udword; break;
164f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    case 64: v = op->lval.uqword; break;
165f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    default: UD_ASSERT(!"invalid offset"); v = 0; /* keep cc happy */
166f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    }
167f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  }
168f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  ud_asmprintf(u, "0x%" FMT64 "x", v);
169f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
170f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
171f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
172f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void
173f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)ud_syn_print_mem_disp(struct ud* u, const struct ud_operand *op, int sign)
174f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles){
175f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  UD_ASSERT(op->offset != 0);
176f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (op->base == UD_NONE && op->index == UD_NONE) {
177f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    uint64_t v;
178f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    UD_ASSERT(op->scale == UD_NONE && op->offset != 8);
179f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    /* unsigned mem-offset */
180f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    switch (op->offset) {
181f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    case 16: v = op->lval.uword;  break;
182f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    case 32: v = op->lval.udword; break;
183f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    case 64: v = op->lval.uqword; break;
184f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    default: UD_ASSERT(!"invalid offset"); v = 0; /* keep cc happy */
185f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    }
186f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    ud_asmprintf(u, "0x%" FMT64 "x", v);
187f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  } else {
188f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    int64_t v;
189f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    UD_ASSERT(op->offset != 64);
190f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    switch (op->offset) {
191f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    case 8 : v = op->lval.sbyte;  break;
192f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    case 16: v = op->lval.sword;  break;
193f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    case 32: v = op->lval.sdword; break;
194f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    default: UD_ASSERT(!"invalid offset"); v = 0; /* keep cc happy */
195f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    }
196f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    if (v < 0) {
197f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      ud_asmprintf(u, "-0x%" FMT64 "x", -v);
198f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    } else if (v > 0) {
199f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      ud_asmprintf(u, "%s0x%" FMT64 "x", sign? "+" : "", v);
200f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    }
201f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  }
202f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
203f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
204f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)/*
205f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)vim: set ts=2 sw=2 expandtab
206f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)*/
207