Merge branch 'master' of ctdb into 'master' of samba
[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 static int max_rows(WINDOW *win)
24 {
25         int maxy, maxx;
26
27         getmaxyx(win, maxy, maxx);
28
29         return maxy - 1;
30 }
31
32 static int hexedit_free(struct hexedit *buf)
33 {
34         if (buf->status_line) {
35                 delwin(buf->status_line);
36         }
37         if (buf->win) {
38                 delwin(buf->win);
39         }
40
41         return 0;
42 }
43
44 struct hexedit *hexedit_new(TALLOC_CTX *ctx, WINDOW *parent, int nlines,
45                             int y, int x, const void *data, size_t sz)
46 {
47         struct hexedit *buf;
48
49         buf = talloc_zero(ctx, struct hexedit);
50         if (buf == NULL) {
51                 return NULL;
52         }
53
54         talloc_set_destructor(buf, hexedit_free);
55
56         buf->data = talloc_zero_array(buf, uint8_t, sz);
57         if (buf->data == NULL) {
58                 goto fail;
59         }
60
61         if (data) {
62                 memcpy(buf->data, data, sz);
63         }
64
65         buf->len = sz;
66         buf->alloc_size = sz;
67         buf->win = derwin(parent, nlines, LINE_WIDTH, y, x);
68         if (buf->win == NULL) {
69                 goto fail;
70         }
71         buf->cursor_x = HEX_COL1;
72
73         buf->status_line = derwin(buf->win, 1, LINE_WIDTH, max_rows(buf->win),
74                                   0);
75         if (buf->status_line == NULL) {
76                 goto fail;
77         }
78         wattron(buf->status_line, A_REVERSE | A_STANDOUT);
79
80         return buf;
81
82 fail:
83         talloc_free(buf);
84
85         return NULL;
86 }
87
88 static size_t bytes_per_screen(WINDOW *win)
89 {
90         return max_rows(win) * BYTES_PER_LINE;
91 }
92
93 void hexedit_set_cursor(struct hexedit *buf)
94 {
95         werase(buf->status_line);
96         if (buf->len) {
97                 wprintw(buf->status_line, "Len:%lu Off:%lu Val:0x%X", buf->len,
98                         buf->cursor_offset, buf->data[buf->cursor_offset]);
99         } else {
100                 wprintw(buf->status_line, "Len:%lu (empty)", buf->len);
101         }
102         wmove(buf->win, buf->cursor_y, buf->cursor_x);
103         wcursyncup(buf->win);
104         wsyncup(buf->win);
105         untouchwin(buf->win);
106 }
107
108 void hexedit_refresh(struct hexedit *buf)
109 {
110         size_t end;
111         size_t lineno;
112         size_t off;
113
114         werase(buf->win);
115         if (buf->len == 0) {
116                 mvwprintw(buf->win, 0, 0, "%08X", 0);
117                 return;
118         }
119
120         end = buf->offset + bytes_per_screen(buf->win);
121         if (end > buf->len) {
122                 end = buf->len;
123         }
124
125         for (off = buf->offset, lineno = 0;
126              off < end;
127              off += BYTES_PER_LINE, ++lineno) {
128                 uint8_t *line = buf->data + off;
129                 size_t i, endline;
130
131                 wmove(buf->win, lineno, 0);
132                 wprintw(buf->win, "%08X  ", off);
133
134                 endline = BYTES_PER_LINE;
135
136                 if (off + BYTES_PER_LINE > buf->len) {
137                         endline = buf->len - off;
138                 }
139
140                 for (i = 0; i < endline; ++i) {
141                         wprintw(buf->win, "%02X", line[i]);
142                         if (i + 1 < endline) {
143                                 if (i == 3) {
144                                         wprintw(buf->win, "  ");
145                                 } else {
146                                         wprintw(buf->win, " ");
147                                 }
148                         }
149                 }
150
151                 wmove(buf->win, lineno, ASCII_COL);
152                 for (i = 0; i < endline; ++i) {
153                         if (isprint(line[i])) {
154                                 waddch(buf->win, line[i]);
155                         } else {
156                                 waddch(buf->win, '.');
157                         }
158                 }
159         }
160 }
161
162 static void calc_cursor_offset(struct hexedit *buf)
163 {
164         buf->cursor_offset = buf->offset + buf->cursor_y * BYTES_PER_LINE +
165                                 buf->cursor_line_offset;
166 }
167
168 static int offset_to_hex_col(size_t pos)
169 {
170         switch (pos) {
171         case 0:
172                 return HEX_COL1;
173         case 1:
174                 return HEX_COL1 + 3;
175         case 2:
176                 return HEX_COL1 + 6;
177         case 3:
178                 return HEX_COL1 + 9;
179
180         case 4:
181                 return HEX_COL2;
182         case 5:
183                 return HEX_COL2 + 3;
184         case 6:
185                 return HEX_COL2 + 6;
186         case 7:
187                 return HEX_COL2 + 9;
188         }
189
190         return -1;
191 }
192
193 static bool scroll_down(struct hexedit *buf)
194 {
195         if (buf->offset + bytes_per_screen(buf->win) >= buf->len) {
196                 return false;
197         }
198
199         buf->offset += BYTES_PER_LINE;
200
201         return true;
202 }
203
204 static bool scroll_up(struct hexedit *buf)
205 {
206         if (buf->offset == 0) {
207                 return false;
208         }
209
210         buf->offset -= BYTES_PER_LINE;
211
212         return true;
213 }
214
215 static void cursor_down(struct hexedit *buf)
216 {
217         if (buf->cursor_y + 1 == max_rows(buf->win)) {
218                 if (scroll_down(buf)) {
219                         hexedit_refresh(buf);
220                 }
221         } else {
222                 if (buf->cursor_offset + BYTES_PER_LINE >= buf->len) {
223                         return;
224                 }
225                 buf->cursor_y++;
226         }
227
228         calc_cursor_offset(buf);
229 }
230
231 static void cursor_up(struct hexedit *buf)
232 {
233         if (buf->cursor_y == 0) {
234                 if (scroll_up(buf)) {
235                         hexedit_refresh(buf);
236                 }
237         } else {
238                 buf->cursor_y--;
239         }
240
241         calc_cursor_offset(buf);
242 }
243
244 static bool is_over_gap(struct hexedit *buf)
245 {
246         int col;
247
248         if (buf->cursor_x < ASCII_COL) {
249                 if (buf->cursor_x >= HEX_COL2) {
250                         col = buf->cursor_x - HEX_COL2;
251                 } else {
252                         col = buf->cursor_x - HEX_COL1;
253                 }
254
255                 switch (col) {
256                 case 2:
257                 case 5:
258                 case 8:
259                         return true;
260                 }
261         }
262
263         return false;
264 }
265
266 static void cursor_left(struct hexedit *buf)
267 {
268         if (buf->cursor_x == HEX_COL1) {
269                 return;
270         }
271         if (buf->cursor_x == HEX_COL2) {
272                 buf->cursor_x = HEX_COL1_END - 1;
273                 buf->cursor_line_offset = 3;
274                 buf->nibble = 1;
275         } else if (buf->cursor_x == ASCII_COL) {
276                 size_t off = buf->offset + buf->cursor_y * BYTES_PER_LINE;
277                 if (off + 7 >= buf->len) {
278                         size_t lastpos = buf->len - off - 1;
279                         buf->cursor_x = offset_to_hex_col(lastpos) + 1;
280                         buf->cursor_line_offset = lastpos;
281                 } else {
282                         buf->cursor_x = HEX_COL2_END - 1;
283                         buf->cursor_line_offset = 7;
284                 }
285                 buf->nibble = 1;
286         } else {
287                 if (buf->cursor_x > ASCII_COL || buf->nibble == 0) {
288                         buf->cursor_line_offset--;
289                 }
290                 buf->cursor_x--;
291                 buf->nibble = !buf->nibble;
292         }
293
294         if (is_over_gap(buf)) {
295                 buf->cursor_x--;
296         }
297
298         calc_cursor_offset(buf);
299 }
300
301 static void cursor_right(struct hexedit *buf)
302 {
303         int new_x = buf->cursor_x + 1;
304
305         if (buf->len == 0) {
306                 return;
307         }
308         if (new_x == ASCII_COL_END) {
309                 return;
310         }
311         if ((buf->cursor_x >= ASCII_COL || buf->nibble == 1) &&
312             buf->cursor_offset + 1 == buf->len) {
313                 if (buf->cursor_x < ASCII_COL) {
314                         new_x = ASCII_COL;
315                         buf->cursor_line_offset = 0;
316                         buf->nibble = 0;
317                 } else {
318                         return;
319                 }
320         }
321         if (new_x == HEX_COL1_END) {
322                 new_x = HEX_COL2;
323                 buf->cursor_line_offset = 4;
324                 buf->nibble = 0;
325         } else if (new_x == HEX_COL2_END) {
326                 new_x = ASCII_COL;
327                 buf->cursor_line_offset = 0;
328                 buf->nibble = 0;
329         } else {
330                 if (buf->cursor_x >= ASCII_COL || buf->nibble == 1) {
331                         buf->cursor_line_offset++;
332                 }
333                 buf->nibble = !buf->nibble;
334         }
335
336         buf->cursor_x = new_x;
337
338         if (is_over_gap(buf)) {
339                 buf->cursor_x++;
340         }
341
342         calc_cursor_offset(buf);
343 }
344
345 static void do_edit(struct hexedit *buf, int c)
346 {
347         uint8_t *byte;
348
349         if (buf->len == 0) {
350                 return;
351         }
352
353         byte = buf->data + buf->cursor_offset;
354
355         if (buf->cursor_x >= ASCII_COL) {
356                 *byte = (uint8_t)c;
357
358                 mvwprintw(buf->win, buf->cursor_y,
359                           offset_to_hex_col(buf->cursor_line_offset), "%X", c);
360                 if (!isprint(c)) {
361                         c = '.';
362                 }
363                 mvwaddch(buf->win, buf->cursor_y,
364                          ASCII_COL + buf->cursor_line_offset, c);
365                 cursor_right(buf);
366         } else {
367                 if (!isxdigit(c)) {
368                         return;
369                 }
370                 c = toupper(c);
371                 waddch(buf->win, c);
372
373                 if (isdigit(c)) {
374                         c = c - '0';
375                 } else {
376                         c = c - 'A' + 10;
377                 }
378                 if (buf->nibble == 0) {
379                         *byte = (*byte & 0x0f) | c << 4;
380                 } else {
381                         *byte = (*byte & 0xf0) | c;
382                 }
383
384                 c = *byte;
385                 if (!isprint(c)) {
386                         c = '.';
387                 }
388                 mvwaddch(buf->win, buf->cursor_y,
389                          ASCII_COL + buf->cursor_line_offset, c);
390
391                 if (buf->cursor_x + 1 != HEX_COL2_END) {
392                         cursor_right(buf);
393                 }
394         }
395 }
396
397 void hexedit_driver(struct hexedit *buf, int c)
398 {
399         switch (c) {
400         case HE_CURSOR_UP:
401                 cursor_up(buf);
402                 break;
403         case HE_CURSOR_DOWN:
404                 cursor_down(buf);
405                 break;
406         case HE_CURSOR_LEFT:
407                 cursor_left(buf);
408                 break;
409         case HE_CURSOR_RIGHT:
410                 cursor_right(buf);
411                 break;
412         case HE_CURSOR_PGUP:
413                 break;
414         case HE_CURSOR_PGDN:
415                 break;
416         default:
417                 do_edit(buf, c & 0xff);
418                 break;
419         }
420 }
421
422 WERROR hexedit_resize_buffer(struct hexedit *buf, size_t newsz)
423 {
424         /* reset the cursor if it'll be out of bounds
425            after the resize */
426         if (buf->cursor_offset >= newsz) {
427                 buf->cursor_y = 0;
428                 buf->cursor_x = HEX_COL1;
429                 buf->offset = 0;
430                 buf->cursor_offset = 0;
431                 buf->cursor_line_offset = 0;
432                 buf->nibble = 0;
433         }
434
435         if (newsz > buf->len) {
436                 if (newsz > buf->alloc_size) {
437                         uint8_t *d;
438                         d = talloc_realloc(buf, buf->data, uint8_t, newsz);
439                         if (d == NULL) {
440                                 return WERR_NOMEM;
441                         }
442                         buf->data = d;
443                         buf->alloc_size = newsz;
444                 }
445                 memset(buf->data + buf->len, '\0', newsz - buf->len);
446                 buf->len = newsz;
447         } else {
448                 buf->len = newsz;
449         }
450
451         return WERR_OK;
452 }