s3:pylibsmb: Make .list() work for SMBv2
[samba.git] / source3 / utils / regedit_hexedit.c
1 /*
2  * Samba Unix/Linux SMB client library
3  * Registry Editor
4  * Copyright (C) Christopher Davis 2012
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include "includes.h"
21 #include "regedit_hexedit.h"
22
23 /*
24  offset    hex1         hex2         ascii
25  00000000  FF FF FF FF  FF FF FF FF  ........
26 */
27
28 #define HEX_COL1 10
29 #define HEX_COL1_END 21
30 #define HEX_COL2 23
31 #define HEX_COL2_END 34
32 #define ASCII_COL 36
33 #define ASCII_COL_END LINE_WIDTH
34 #define BYTES_PER_LINE 8
35
36 struct hexedit {
37         size_t offset;
38         size_t len;
39         size_t alloc_size;
40         int cursor_y;
41         int cursor_x;
42         size_t cursor_offset;
43         size_t cursor_line_offset;
44         int nibble;
45         uint8_t *data;
46         WINDOW *win;
47 };
48
49 static int max_rows(WINDOW *win)
50 {
51         int maxy;
52
53         maxy = getmaxy(win);
54
55         return maxy - 1;
56 }
57
58 struct hexedit *hexedit_new(TALLOC_CTX *ctx, WINDOW *parent, const void *data,
59                             size_t sz)
60 {
61         WERROR rv;
62         struct hexedit *buf;
63
64         buf = talloc_zero(ctx, struct hexedit);
65         if (buf == NULL) {
66                 return NULL;
67         }
68
69         buf->win = parent;
70
71         rv = hexedit_set_buf(buf, data, sz);
72         if (!W_ERROR_IS_OK(rv)) {
73                 goto fail;
74         }
75
76         return buf;
77
78 fail:
79         talloc_free(buf);
80
81         return NULL;
82 }
83
84 WERROR hexedit_set_buf(struct hexedit *buf, const void *data, size_t sz)
85 {
86         TALLOC_FREE(buf->data);
87
88         buf->data = talloc_zero_array(buf, uint8_t, sz);
89         if (buf->data == NULL) {
90                 return WERR_NOT_ENOUGH_MEMORY;
91         }
92
93         if (data != NULL) {
94                 memcpy(buf->data, data, sz);
95         }
96
97         buf->len = sz;
98         buf->alloc_size = sz;
99         buf->cursor_x = HEX_COL1;
100         buf->cursor_y = 0;
101         buf->cursor_offset = 0;
102         buf->cursor_line_offset = 0;
103         buf->nibble = 0;
104
105         return WERR_OK;
106 }
107
108 const void *hexedit_get_buf(struct hexedit *buf)
109 {
110         return buf->data;
111 }
112
113 size_t hexedit_get_buf_len(struct hexedit *buf)
114 {
115         return buf->len;
116 }
117
118 static size_t bytes_per_screen(WINDOW *win)
119 {
120         return max_rows(win) * BYTES_PER_LINE;
121 }
122
123 void hexedit_set_cursor(struct hexedit *buf)
124 {
125         wmove(buf->win, max_rows(buf->win), 0);
126         wattron(buf->win, A_REVERSE | A_STANDOUT);
127         wclrtoeol(buf->win);
128         if (buf->cursor_offset < buf->len) {
129                 wprintw(buf->win, "Len:%lu Off:%lu Val:0x%X", buf->len,
130                         buf->cursor_offset, buf->data[buf->cursor_offset]);
131         } else {
132                 wprintw(buf->win, "Len:%lu Off:%lu", buf->len,
133                         buf->cursor_offset);
134         }
135         wattroff(buf->win, A_REVERSE | A_STANDOUT);
136         wmove(buf->win, buf->cursor_y, buf->cursor_x);
137         wcursyncup(buf->win);
138         wsyncup(buf->win);
139         untouchwin(buf->win);
140 }
141
142 void hexedit_refresh(struct hexedit *buf)
143 {
144         size_t end;
145         size_t lineno;
146         size_t off;
147
148         werase(buf->win);
149         if (buf->len == 0) {
150                 mvwprintw(buf->win, 0, 0, "%08X", 0);
151                 return;
152         }
153
154         end = buf->offset + bytes_per_screen(buf->win);
155         if (end > buf->len) {
156                 end = buf->len;
157         }
158
159         for (off = buf->offset, lineno = 0;
160              off < end;
161              off += BYTES_PER_LINE, ++lineno) {
162                 uint8_t *line = buf->data + off;
163                 size_t i, endline;
164
165                 wmove(buf->win, lineno, 0);
166                 wprintw(buf->win, "%08X  ", off);
167
168                 endline = BYTES_PER_LINE;
169
170                 if (off + BYTES_PER_LINE > buf->len) {
171                         endline = buf->len - off;
172                 }
173
174                 for (i = 0; i < endline; ++i) {
175                         wprintw(buf->win, "%02X", line[i]);
176                         if (i + 1 < endline) {
177                                 if (i == 3) {
178                                         wprintw(buf->win, "  ");
179                                 } else {
180                                         wprintw(buf->win, " ");
181                                 }
182                         }
183                 }
184
185                 wmove(buf->win, lineno, ASCII_COL);
186                 for (i = 0; i < endline; ++i) {
187                         if (isprint(line[i])) {
188                                 waddch(buf->win, line[i]);
189                         } else {
190                                 waddch(buf->win, '.');
191                         }
192                 }
193         }
194 }
195
196 static void calc_cursor_offset(struct hexedit *buf)
197 {
198         buf->cursor_offset = buf->offset + buf->cursor_y * BYTES_PER_LINE +
199                                 buf->cursor_line_offset;
200 }
201
202 static int offset_to_hex_col(size_t pos)
203 {
204         switch (pos) {
205         case 0:
206                 return HEX_COL1;
207         case 1:
208                 return HEX_COL1 + 3;
209         case 2:
210                 return HEX_COL1 + 6;
211         case 3:
212                 return HEX_COL1 + 9;
213
214         case 4:
215                 return HEX_COL2;
216         case 5:
217                 return HEX_COL2 + 3;
218         case 6:
219                 return HEX_COL2 + 6;
220         case 7:
221                 return HEX_COL2 + 9;
222         }
223
224         return -1;
225 }
226
227 static bool scroll_up(struct hexedit *buf)
228 {
229         if (buf->offset == 0) {
230                 return false;
231         }
232
233         buf->offset -= BYTES_PER_LINE;
234
235         return true;
236 }
237
238 static void cursor_down(struct hexedit *buf)
239 {
240         size_t space;
241         bool need_refresh = false;
242
243         space = buf->offset + (buf->cursor_y + 1) * BYTES_PER_LINE;
244         if (space > buf->len) {
245                 return;
246         }
247
248         if (buf->cursor_y + 1 == max_rows(buf->win)) {
249                 buf->offset += BYTES_PER_LINE;
250                 need_refresh = true;
251         } else {
252                 buf->cursor_y++;
253         }
254
255         if (buf->cursor_offset + BYTES_PER_LINE > buf->len) {
256                 buf->nibble = 0;
257                 buf->cursor_offset = buf->len;
258                 buf->cursor_line_offset = buf->len - space;
259                 if (buf->cursor_x >= ASCII_COL) {
260                         buf->cursor_x = ASCII_COL + buf->cursor_line_offset;
261                 } else {
262                         buf->cursor_x = offset_to_hex_col(buf->cursor_line_offset);
263                 }
264         }
265         if (need_refresh) {
266                 hexedit_refresh(buf);
267         }
268         calc_cursor_offset(buf);
269 }
270
271 static void cursor_up(struct hexedit *buf)
272 {
273         if (buf->cursor_y == 0) {
274                 if (scroll_up(buf)) {
275                         hexedit_refresh(buf);
276                 }
277         } else {
278                 buf->cursor_y--;
279         }
280
281         calc_cursor_offset(buf);
282 }
283
284 static bool is_over_gap(struct hexedit *buf)
285 {
286         int col;
287
288         if (buf->cursor_x < ASCII_COL) {
289                 if (buf->cursor_x >= HEX_COL2) {
290                         col = buf->cursor_x - HEX_COL2;
291                 } else {
292                         col = buf->cursor_x - HEX_COL1;
293                 }
294
295                 switch (col) {
296                 case 2:
297                 case 5:
298                 case 8:
299                         return true;
300                 }
301         }
302
303         return false;
304 }
305
306 static void cursor_left(struct hexedit *buf)
307 {
308         if (buf->cursor_x == HEX_COL1) {
309                 return;
310         }
311         if (buf->cursor_x == HEX_COL2) {
312                 buf->cursor_x = HEX_COL1_END - 1;
313                 buf->cursor_line_offset = 3;
314                 buf->nibble = 1;
315         } else if (buf->cursor_x == ASCII_COL) {
316                 size_t off = buf->offset + buf->cursor_y * BYTES_PER_LINE;
317                 if (off + 7 >= buf->len) {
318                         size_t lastpos = buf->len - off;
319                         buf->cursor_x = offset_to_hex_col(lastpos) + 1;
320                         buf->cursor_line_offset = lastpos;
321                 } else {
322                         buf->cursor_x = HEX_COL2_END - 1;
323                         buf->cursor_line_offset = 7;
324                 }
325                 buf->nibble = 1;
326         } else {
327                 if (buf->cursor_x > ASCII_COL || buf->nibble == 0) {
328                         buf->cursor_line_offset--;
329                 }
330                 buf->cursor_x--;
331                 buf->nibble = !buf->nibble;
332         }
333
334         if (is_over_gap(buf)) {
335                 buf->cursor_x--;
336         }
337
338         calc_cursor_offset(buf);
339 }
340
341 static void cursor_right(struct hexedit *buf)
342 {
343         int new_x = buf->cursor_x + 1;
344
345         if (new_x == ASCII_COL_END) {
346                 return;
347         }
348         if ((buf->cursor_x >= ASCII_COL || buf->nibble == 1) &&
349             buf->cursor_offset == buf->len) {
350                 if (buf->cursor_x < ASCII_COL) {
351                         new_x = ASCII_COL;
352                         buf->cursor_line_offset = 0;
353                         buf->nibble = 0;
354                 } else {
355                         return;
356                 }
357         }
358         if (new_x == HEX_COL1_END) {
359                 new_x = HEX_COL2;
360                 buf->cursor_line_offset = 4;
361                 buf->nibble = 0;
362         } else if (new_x == HEX_COL2_END) {
363                 new_x = ASCII_COL;
364                 buf->cursor_line_offset = 0;
365                 buf->nibble = 0;
366         } else {
367                 if (buf->cursor_x >= ASCII_COL || buf->nibble == 1) {
368                         buf->cursor_line_offset++;
369                 }
370                 buf->nibble = !buf->nibble;
371         }
372
373         buf->cursor_x = new_x;
374
375         if (is_over_gap(buf)) {
376                 buf->cursor_x++;
377         }
378
379         calc_cursor_offset(buf);
380 }
381
382 static void do_edit(struct hexedit *buf, int c)
383 {
384         uint8_t *byte;
385
386         if (buf->cursor_offset == buf->len) {
387                 hexedit_resize_buffer(buf, buf->len + 1);
388         }
389
390         byte = buf->data + buf->cursor_offset;
391
392         if (buf->cursor_x >= ASCII_COL) {
393                 *byte = (uint8_t)c;
394
395                 mvwprintw(buf->win, buf->cursor_y,
396                           offset_to_hex_col(buf->cursor_line_offset), "%X", c);
397                 if (!isprint(c)) {
398                         c = '.';
399                 }
400                 mvwaddch(buf->win, buf->cursor_y,
401                          ASCII_COL + buf->cursor_line_offset, c);
402                 if (buf->cursor_x + 1 != ASCII_COL_END) {
403                         cursor_right(buf);
404                 } else {
405                         cursor_down(buf);
406                 }
407         } else {
408                 if (!isxdigit(c)) {
409                         return;
410                 }
411                 c = toupper(c);
412                 waddch(buf->win, c);
413
414                 if (isdigit(c)) {
415                         c = c - '0';
416                 } else {
417                         c = c - 'A' + 10;
418                 }
419                 if (buf->nibble == 0) {
420                         *byte = (*byte & 0x0f) | c << 4;
421                 } else {
422                         *byte = (*byte & 0xf0) | c;
423                 }
424
425                 c = *byte;
426                 if (!isprint(c)) {
427                         c = '.';
428                 }
429                 mvwaddch(buf->win, buf->cursor_y,
430                          ASCII_COL + buf->cursor_line_offset, c);
431
432                 if (buf->cursor_x + 1 != HEX_COL2_END) {
433                         cursor_right(buf);
434                 } else {
435                         cursor_down(buf);
436                 }
437         }
438
439         hexedit_refresh(buf);
440 }
441
442 static void erase_at(struct hexedit *buf, size_t pos)
443 {
444         if (pos >= buf->len) {
445                 return;
446         }
447
448         if (pos < buf->len - 1) {
449                 /* squeeze the character out of the buffer */
450                 uint8_t *p = buf->data + pos;
451                 uint8_t *end = buf->data + buf->len;
452                 memmove(p, p + 1, end - p - 1);
453         }
454
455         buf->len--;
456         hexedit_refresh(buf);
457 }
458
459 static void do_backspace(struct hexedit *buf)
460 {
461         size_t off;
462         bool do_erase = true;
463
464         if (buf->cursor_offset == 0) {
465                 return;
466         }
467
468         off = buf->cursor_offset;
469         if (buf->cursor_x == ASCII_COL) {
470                 cursor_up(buf);
471                 buf->cursor_line_offset = 7;
472                 buf->cursor_x = ASCII_COL_END - 1;
473                 calc_cursor_offset(buf);
474         } else if (buf->cursor_x == HEX_COL1) {
475                 cursor_up(buf);
476                 buf->cursor_line_offset = 7;
477                 buf->cursor_x = HEX_COL2_END - 1;
478                 buf->nibble = 1;
479                 calc_cursor_offset(buf);
480         } else {
481                 if (buf->cursor_x < ASCII_COL && buf->nibble) {
482                         do_erase = false;
483                 }
484                 cursor_left(buf);
485         }
486         if (do_erase) {
487                 erase_at(buf, off - 1);
488         }
489 }
490
491 static void do_delete(struct hexedit *buf)
492 {
493         erase_at(buf, buf->cursor_offset);
494 }
495
496 void hexedit_driver(struct hexedit *buf, int c)
497 {
498         switch (c) {
499         case HE_CURSOR_UP:
500                 cursor_up(buf);
501                 break;
502         case HE_CURSOR_DOWN:
503                 cursor_down(buf);
504                 break;
505         case HE_CURSOR_LEFT:
506                 cursor_left(buf);
507                 break;
508         case HE_CURSOR_RIGHT:
509                 cursor_right(buf);
510                 break;
511         case HE_CURSOR_PGUP:
512                 break;
513         case HE_CURSOR_PGDN:
514                 break;
515         case HE_BACKSPACE:
516                 do_backspace(buf);
517                 break;
518         case HE_DELETE:
519                 do_delete(buf);
520                 break;
521         default:
522                 do_edit(buf, c & 0xff);
523                 break;
524         }
525
526         hexedit_set_cursor(buf);
527 }
528
529 WERROR hexedit_resize_buffer(struct hexedit *buf, size_t newsz)
530 {
531         /* reset the cursor if it'll be out of bounds
532            after the resize */
533         if (buf->cursor_offset > newsz) {
534                 buf->cursor_y = 0;
535                 buf->cursor_x = HEX_COL1;
536                 buf->offset = 0;
537                 buf->cursor_offset = 0;
538                 buf->cursor_line_offset = 0;
539                 buf->nibble = 0;
540         }
541
542         if (newsz > buf->len) {
543                 if (newsz > buf->alloc_size) {
544                         uint8_t *d;
545                         buf->alloc_size *= 2;
546                         if (newsz > buf->alloc_size) {
547                                 buf->alloc_size = newsz;
548                         }
549                         d = talloc_realloc(buf, buf->data, uint8_t,
550                                            buf->alloc_size);
551                         if (d == NULL) {
552                                 return WERR_NOT_ENOUGH_MEMORY;
553                         }
554                         buf->data = d;
555                 }
556                 memset(buf->data + buf->len, '\0', newsz - buf->len);
557                 buf->len = newsz;
558         } else {
559                 buf->len = newsz;
560         }
561
562         return WERR_OK;
563 }