Bug Summary

File:home/HaikuArchives/ArtPaint/artpaint/application/UndoQueue.cpp
Warning:line 471, column 13
Use of memory after it is freed

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-haiku -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name UndoQueue.cpp -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model static -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -tune-cpu generic -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /boot/system/lib/clang/12.0.1 -iquote ./ -iquote artpaint/ -iquote artpaint/Utilities/ -iquote artpaint/application/ -iquote artpaint/controls/ -iquote artpaint/layers/ -iquote artpaint/paintwindow/ -iquote artpaint/tools/ -iquote artpaint/viewmanipulators/ -iquote artpaint/windows/ -iquote objects_artpaint/ -isystem /boot/system/develop/headers/private/interface -internal-isystem /system/develop/headers/c++ -internal-isystem /system/develop/headers/c++/x86_64-unknown-haiku -internal-isystem /system/develop/headers/c++/backward -O3 -fdeprecated-macro -fdebug-compilation-dir /boot/home/HaikuArchives/ArtPaint -ferror-limit 19 -fgnuc-version=4.2.1 -fcxx-exceptions -fexceptions -vectorize-loops -vectorize-slp -analyzer-output=html -faddrsig -o /tmp/scan-build-2022-06-19-103017-1294-1 -x c++ artpaint/application/UndoQueue.cpp
1/*
2 * Copyright 2003, Heikki Suhonen
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 * Heikki Suhonen <heikki.suhonen@gmail.com>
7 * Dale Cieslak <dcieslak@yahoo.com>
8 *
9 */
10
11#include "UndoQueue.h"
12
13#include "Image.h"
14#include "Selection.h"
15#include "SettingsServer.h"
16
17
18#include <Alert.h>
19#include <Catalog.h>
20#include <MenuItem.h>
21
22
23#include <new>
24#include <stdio.h>
25
26
27#undef B_TRANSLATION_CONTEXT"UndoQueue"
28#define B_TRANSLATION_CONTEXT"UndoQueue" "UndoQueue"
29
30int32 UndoQueue::maximum_queue_depth = 10;
31BList* UndoQueue::queue_list = new BList();
32
33
34UndoQueue::UndoQueue(BMenuItem *undo_item,BMenuItem *redo_item,ImageView *iv)
35{
36 image_view = iv;
37 layer_bitmaps = NULL__null;
38 layer_bitmap_count = 0;
39
40 undo_menu_item = undo_item;
41 redo_menu_item = redo_item;
42 current_event = NULL__null;
43 first_event = NULL__null;
44 last_event = NULL__null;
45
46 current_queue_depth = 0;
47
48 queue_list->AddItem(this);
49
50 selection_data = new SelectionData();
51
52 UpdateMenuItems();
53}
54
55
56UndoQueue::UndoQueue(BFile&)
57{
58}
59
60
61UndoQueue::~UndoQueue()
62{
63 UndoEvent *spare_event = first_event;
64 while (spare_event != NULL__null) {
65 first_event = spare_event->next_event;
66 delete spare_event;
67 spare_event = first_event;
68 }
69
70 queue_list->RemoveItem(this);
71
72 for (int32 i=0;i<layer_bitmap_count;i++) {
73 delete layer_bitmaps[i];
74 layer_bitmaps[i] = NULL__null;
75 }
76 delete[] layer_bitmaps;
77
78 delete selection_data;
79}
80
81
82UndoEvent* UndoQueue::AddUndoEvent(const char *name,const BBitmap *thumbnail,bool remove_tail)
83{
84 if (maximum_queue_depth == 0)
85 return NULL__null;
86
87 UndoEvent *event = new UndoEvent(name,thumbnail);
88 current_queue_depth++;
89
90 if (remove_tail == FALSE0) {
91 // Insert the new event in between.
92 if (current_event != NULL__null) {
93 event->next_event = current_event->next_event;
94 event->previous_event = current_event;
95 event->next_event->previous_event = event;
96 current_event->next_event = event;
97 }
98 }
99 else {
100 UndoEvent *spare_event;
101 if (current_event != NULL__null)
102 spare_event = current_event->next_event;
103 else {
104 spare_event = first_event;
105 first_event = NULL__null;
106 }
107 // Delete the rest of the events
108 while (spare_event != NULL__null) {
109 UndoEvent *another_spare_event = spare_event->next_event;
110 delete spare_event;
111 spare_event = another_spare_event;
112 current_queue_depth--;
113 }
114
115 if (current_event != NULL__null) {
116 current_event->next_event = event;
117 event->previous_event = current_event;
118 }
119 }
120
121 if (first_event == NULL__null)
122 first_event = event;
123 current_event = event;
124
125 event->SetQueue(this);
126
127 TruncateQueue();
128
129 UpdateMenuItems();
130 return event;
131}
132
133
134status_t UndoQueue::RemoveEvent(UndoEvent *event)
135{
136 current_queue_depth--;
137
138 UndoEvent *spare_event = first_event;
139 while ((spare_event != event) && (spare_event != NULL__null)) {
140 spare_event = spare_event->next_event;
141 }
142 if (spare_event == NULL__null)
143 return B_ERROR(-1);
144
145
146 // If the current event is the event that is removed we must change the
147 // current event also. At the moment this should happen always.
148 if (current_event == spare_event) {
149 if (event->previous_event != NULL__null)
150 current_event = spare_event->previous_event;
151 else
152 current_event = spare_event->next_event;
153 }
154
155
156 // If the first event is the removed one, we should also update its
157 // status.
158 if (first_event == spare_event) {
159 first_event = spare_event->next_event;
160 }
161
162 // Unlink the event
163 if (spare_event->previous_event != NULL__null) {
164 spare_event->previous_event->next_event = spare_event->next_event;
165 }
166 if (spare_event->next_event != NULL__null) {
167 spare_event->next_event->previous_event = spare_event->previous_event;
168 }
169
170
171 UpdateMenuItems();
172 return B_OK((int)0);
173}
174
175BBitmap* UndoQueue::ReturnLayerSpareBitmap(int32 layer_id,BBitmap *layer_bitmap)
176{
177 if (layer_id > layer_bitmap_count-1) {
178 BBitmap **new_bitmaps = new BBitmap*[layer_id+1];
179 for (int32 i=0;i<layer_id+1;i++)
180 new_bitmaps[i] = NULL__null;
181
182 for (int32 i=0;i<layer_bitmap_count;i++) {
183 new_bitmaps[i] = layer_bitmaps[i];
184 layer_bitmaps[i] = NULL__null;
185 }
186
187 delete[] layer_bitmaps;
188 layer_bitmaps = new_bitmaps;
189 layer_bitmap_count = layer_id+1;
190 }
191
192 if (layer_bitmaps[layer_id] == NULL__null) {
193 if (layer_bitmap != NULL__null) {
194 layer_bitmaps[layer_id] = new BBitmap(layer_bitmap->Bounds(),B_RGB32);
195 if (layer_bitmaps[layer_id]->IsValid() == FALSE0)
196 throw std::bad_alloc();
197 }
198 }
199
200 return layer_bitmaps[layer_id];
201}
202
203
204status_t UndoQueue::ChangeLayerSpareBitmap(int32 layer_id, BBitmap *layer_bitmap)
205{
206 // This function doesn't delete the old bitmap.
207 if (layer_id > layer_bitmap_count-1) {
208 BBitmap **new_bitmaps = new BBitmap*[layer_id+1];
209 for (int32 i=0;i<layer_id+1;i++)
210 new_bitmaps[i] = NULL__null;
211
212 for (int32 i=0;i<layer_bitmap_count;i++) {
213 new_bitmaps[i] = layer_bitmaps[i];
214 layer_bitmaps[i] = NULL__null;
215 }
216 delete[] layer_bitmaps;
217 layer_bitmaps = new_bitmaps;
218 layer_bitmap_count = layer_id+1;
219 }
220
221 if (layer_bitmap != NULL__null) {
222 layer_bitmaps[layer_id] = new BBitmap(layer_bitmap->Bounds(),B_RGB32);
223 if (layer_bitmaps[layer_id]->IsValid() == FALSE0)
224 throw std::bad_alloc();
225
226 uint32 *spare_bits = (uint32*)layer_bitmaps[layer_id]->Bits();
227 uint32 *bits = (uint32*)layer_bitmap->Bits();
228 uint32 bitslength = layer_bitmap->BitsLength()/4;
229 for (uint32 i = 0; i < bitslength; i++)
230 *spare_bits++ = *bits++;
231 }
232 else {
233 layer_bitmaps[layer_id] = NULL__null;
234 }
235 return B_OK((int)0);
236}
237
238
239
240void UndoQueue::RegisterLayer(int32 layer_id,BBitmap *layer_bitmap)
241{
242 if ((maximum_queue_depth > 0) || (maximum_queue_depth == INFINITE_QUEUE_DEPTH-1)) {
243 if (layer_id > layer_bitmap_count-1) {
244 BBitmap **new_bitmaps = new BBitmap*[layer_id+1];
245 for (int32 i=0;i<layer_id+1;i++)
246 new_bitmaps[i] = NULL__null;
247
248 for (int32 i=0;i<layer_bitmap_count;i++) {
249 new_bitmaps[i] = layer_bitmaps[i];
250 layer_bitmaps[i] = NULL__null;
251 }
252 delete[] layer_bitmaps;
253 layer_bitmaps = new_bitmaps;
254 layer_bitmap_count = layer_id+1;
255
256 }
257
258 if (layer_bitmaps[layer_id] == NULL__null) {
259 if (layer_bitmap != NULL__null) {
260 layer_bitmaps[layer_id] = new BBitmap(layer_bitmap->Bounds(),B_RGB32);
261 if (layer_bitmaps[layer_id]->IsValid() == FALSE0)
262 throw std::bad_alloc();
263
264 uint32 *source_bits = (uint32*)layer_bitmap->Bits();
265 uint32 *target_bits = (uint32*)layer_bitmaps[layer_id]->Bits();
266 int32 bitslength = layer_bitmap->BitsLength()/4;
267
268 for (int32 i=0;i<bitslength;i++)
269 *target_bits++ = *source_bits++;
270 }
271 }
272 }
273}
274
275
276UndoEvent* UndoQueue::Undo()
277{
278 UndoEvent *returned_event = NULL__null;
279
280 if (current_event != NULL__null) {
281 returned_event = current_event;
282 current_event = current_event->previous_event;
283 if (returned_event->ActionCount() == 1) {
284 if (returned_event->ReturnActions()[0]->type == ADD_LAYER_ACTION) {
285 // This event should be removed.
286 current_event = returned_event;
287 returned_event = NULL__null;
288 }
289 }
290 }
291
292 UpdateMenuItems();
293 return returned_event;
294}
295
296
297UndoEvent* UndoQueue::Redo()
298{
299 UndoEvent *returned_event = NULL__null;
300 if (current_event == NULL__null)
301 returned_event = current_event = first_event;
302 else {
303 if (current_event->next_event != NULL__null) {
304 current_event = current_event->next_event;
305 returned_event = current_event;
306 }
307 }
308 UpdateMenuItems();
309 return returned_event;
310}
311
312
313
314const char* UndoQueue::ReturnUndoEventName()
315{
316 const char *name = NULL__null;
317
318 UndoEvent *named_event = current_event;
319 if (named_event != NULL__null) {
320 UndoAction **actions = named_event->ReturnActions();
321 if (actions != NULL__null) {
322 if ((named_event->ActionCount() > 1) || (actions[0]->type != ADD_LAYER_ACTION)) {
323 name = named_event->ReturnName();
324 }
325 }
326 else
327 name = named_event->ReturnName();
328 }
329
330 return name;
331}
332
333
334const char* UndoQueue::ReturnRedoEventName()
335{
336 const char *name = NULL__null;
337
338 UndoEvent *named_event;
339 if (current_event != NULL__null) {
340 named_event = current_event->next_event;
341 }
342 else
343 named_event = first_event;
344
345 if (named_event != NULL__null)
346 name = named_event->ReturnName();
347
348 return name;
349}
350
351
352
353void UndoQueue::UpdateMenuItems()
354{
355 const char *event_name;
356 event_name = ReturnUndoEventName();
357
358 if (undo_menu_item != NULL__null) {
359 if (event_name != NULL__null) {
360 char menu_text[256];
361 sprintf(menu_text,"%s %s",B_TRANSLATE("Undo")BLocaleRoster::Default()->GetCatalog()->GetString(("Undo"
), "UndoQueue")
,event_name);
362 undo_menu_item->SetLabel(menu_text);
363 undo_menu_item->SetEnabled(TRUE1);
364 }
365 else {
366 undo_menu_item->SetLabel(B_TRANSLATE("Undo not available")BLocaleRoster::Default()->GetCatalog()->GetString(("Undo not available"
), "UndoQueue")
);
367 undo_menu_item->SetEnabled(FALSE0);
368 }
369 }
370
371 event_name = ReturnRedoEventName();
372 if (redo_menu_item != NULL__null) {
373 if (event_name != NULL__null) {
374 char menu_text[256];
375 sprintf(menu_text,"%s %s",B_TRANSLATE("Redo")BLocaleRoster::Default()->GetCatalog()->GetString(("Redo"
), "UndoQueue")
,event_name);
376 redo_menu_item->SetLabel(menu_text);
377 redo_menu_item->SetEnabled(TRUE1);
378 }
379 else {
380 redo_menu_item->SetLabel(B_TRANSLATE("Redo not available")BLocaleRoster::Default()->GetCatalog()->GetString(("Redo not available"
), "UndoQueue")
);
381 redo_menu_item->SetEnabled(FALSE0);
382 }
383 }
384}
385
386
387void UndoQueue::SetMenuItems(BMenuItem *undo_item,BMenuItem *redo_item)
388{
389 undo_menu_item = undo_item;
390 redo_menu_item = redo_item;
391 UpdateMenuItems();
392}
393
394
395void UndoQueue::HandleLowMemorySituation()
396{
397 BAlert *memory_alert = new BAlert("memory_alert", B_TRANSLATE(BLocaleRoster::Default()->GetCatalog()->GetString(("The undo-mechanism has run out of memory.\n"
"The depth of undo will be limited so that the most recent events can be kept in memory. "
"It is advisable to save your work at this point to avoid any loss of data in case the "
"memory runs out completely.\n" "You may also want to adjust the undo-depth in the settings-window."
), "UndoQueue")
398 "The undo-mechanism has run out of memory.\n"BLocaleRoster::Default()->GetCatalog()->GetString(("The undo-mechanism has run out of memory.\n"
"The depth of undo will be limited so that the most recent events can be kept in memory. "
"It is advisable to save your work at this point to avoid any loss of data in case the "
"memory runs out completely.\n" "You may also want to adjust the undo-depth in the settings-window."
), "UndoQueue")
399 "The depth of undo will be limited so that the most recent events can be kept in memory. "BLocaleRoster::Default()->GetCatalog()->GetString(("The undo-mechanism has run out of memory.\n"
"The depth of undo will be limited so that the most recent events can be kept in memory. "
"It is advisable to save your work at this point to avoid any loss of data in case the "
"memory runs out completely.\n" "You may also want to adjust the undo-depth in the settings-window."
), "UndoQueue")
400 "It is advisable to save your work at this point to avoid any loss of data in case the "BLocaleRoster::Default()->GetCatalog()->GetString(("The undo-mechanism has run out of memory.\n"
"The depth of undo will be limited so that the most recent events can be kept in memory. "
"It is advisable to save your work at this point to avoid any loss of data in case the "
"memory runs out completely.\n" "You may also want to adjust the undo-depth in the settings-window."
), "UndoQueue")
401 "memory runs out completely.\n"BLocaleRoster::Default()->GetCatalog()->GetString(("The undo-mechanism has run out of memory.\n"
"The depth of undo will be limited so that the most recent events can be kept in memory. "
"It is advisable to save your work at this point to avoid any loss of data in case the "
"memory runs out completely.\n" "You may also want to adjust the undo-depth in the settings-window."
), "UndoQueue")
402 "You may also want to adjust the undo-depth in the settings-window.")BLocaleRoster::Default()->GetCatalog()->GetString(("The undo-mechanism has run out of memory.\n"
"The depth of undo will be limited so that the most recent events can be kept in memory. "
"It is advisable to save your work at this point to avoid any loss of data in case the "
"memory runs out completely.\n" "You may also want to adjust the undo-depth in the settings-window."
), "UndoQueue")
,
403 B_TRANSLATE("Bummer")BLocaleRoster::Default()->GetCatalog()->GetString(("Bummer"
), "UndoQueue")
, NULL__null, NULL__null, B_WIDTH_AS_USUAL,B_WARNING_ALERT);
404 memory_alert->Go();
405
406 // We may have to delete either redo-events or undo-events. Currently this function is called
407 // only when there are no redo events so we only delete undo-events. This may change in the
408 // future however.
409 int32 deleted_event_count = 0;
410 UndoEvent *deleted_event = first_event;
411 while ((deleted_event_count < 5) && (deleted_event != NULL__null) && (deleted_event != current_event)) {
412 first_event = deleted_event->next_event;
413 first_event->previous_event = NULL__null;
414 delete deleted_event;
415 deleted_event = first_event;
416 deleted_event_count++;
417 current_queue_depth--;
418 }
419}
420
421
422void
423UndoQueue::SetQueueDepth(int32 depth)
424{
425 if ((depth != INFINITE_QUEUE_DEPTH-1)
1
Assuming the condition is true
4
Taking true branch
426 && ((depth < maximum_queue_depth)
2
Assuming 'depth' is >= 'maximum_queue_depth'
427 || (maximum_queue_depth == INFINITE_QUEUE_DEPTH-1))) {
3
Assuming the condition is true
428 maximum_queue_depth = depth;
429 for (int32 i = 0; i < queue_list->CountItems(); i++) {
5
Assuming the condition is true
6
Loop condition is true. Entering loop body
430 UndoQueue *queue = (UndoQueue*)queue_list->ItemAt(i);
431 queue->TruncateQueue();
7
Calling 'UndoQueue::TruncateQueue'
432 }
433 }
434
435 if ((maximum_queue_depth == 0) && (depth != 0)) {
436 maximum_queue_depth = depth;
437 for (int32 i = 0; i < queue_list->CountItems(); i++) {
438 UndoQueue *queue = (UndoQueue*)queue_list->ItemAt(i);
439 queue->image_view->ReturnImage()->RegisterLayersWithUndo();
440 }
441 }
442
443 maximum_queue_depth = depth;
444
445 if (SettingsServer* server = SettingsServer::Instance())
446 server->SetValue(SettingsServer::Application, skUndoQueueDepth, depth);
447}
448
449
450void
451UndoQueue::TruncateQueue()
452{
453 if ((maximum_queue_depth != INFINITE_QUEUE_DEPTH-1) && (current_queue_depth > maximum_queue_depth)) {
8
Assuming 'maximum_queue_depth' is < field 'current_queue_depth'
9
Taking true branch
454 while (current_queue_depth > maximum_queue_depth
9.1
'maximum_queue_depth' is < field 'current_queue_depth'
) {
10
Loop condition is true. Entering loop body
29
Assuming 'maximum_queue_depth' is < field 'current_queue_depth'
30
Loop condition is true. Entering loop body
455 // We should remove the events so that the nearest events to the current
456 // event from both sides are removed last, and the furthest away events
457 // first. If needed the actual current event is then removed at the end.
458
459 // search the furhest away event from the current_event, or if current event
460 // is NULL, from the first_event. If both are NULL, put the current_queue_length to
461 // zero.
462 UndoEvent *furthest_event = NULL__null;
463 if (current_event
30.1
Field 'current_event' is not equal to NULL
!= NULL__null
) {
11
Assuming field 'current_event' is not equal to NULL
12
Taking true branch
31
Taking true branch
464 UndoEvent *redo_direction=current_event;
465 UndoEvent *undo_direction=current_event;
466 while ((undo_direction
12.1
'undo_direction' is not equal to NULL
31.1
'undo_direction' is not equal to NULL
32.1
'undo_direction' is not equal to NULL
!= NULL__null
) && (redo_direction
12.2
'redo_direction' is not equal to NULL
31.2
'redo_direction' is not equal to NULL
!= NULL__null
)) {
13
Loop condition is true. Entering loop body
14
Assuming 'undo_direction' is not equal to NULL
15
Assuming 'redo_direction' is equal to NULL
16
Loop condition is false. Execution continues on line 470
32
Loop condition is true. Entering loop body
33
Loop condition is false. Execution continues on line 470
467 undo_direction = undo_direction->previous_event;
468 redo_direction = redo_direction->next_event;
469 }
470 if (undo_direction
16.1
'undo_direction' is not equal to NULL
33.1
'undo_direction' is not equal to NULL
!= NULL__null) {
17
Taking true branch
34
Taking true branch
471 while (undo_direction->previous_event != NULL__null)
18
Assuming field 'previous_event' is equal to NULL
19
Loop condition is false. Execution continues on line 473
35
Use of memory after it is freed
472 undo_direction = undo_direction->previous_event;
473 furthest_event = undo_direction;
474 }
475 else if (redo_direction != NULL__null) {
476 while (redo_direction->next_event != NULL__null)
477 redo_direction = redo_direction->next_event;
478 furthest_event = redo_direction;
479 }
480 else
481 furthest_event = first_event;
482 }
483 else if (first_event != NULL__null) {
484 furthest_event = first_event;
485 while (furthest_event->next_event != NULL__null)
486 furthest_event = furthest_event->next_event;
487 }
488 else
489 current_queue_depth = 0;
490
491 if (furthest_event == first_event) {
20
Assuming 'furthest_event' is not equal to field 'first_event'
21
Taking false branch
492 if (first_event != NULL__null)
493 first_event = first_event->next_event;
494 }
495
496 if (current_event == furthest_event)
22
Assuming 'furthest_event' is not equal to field 'current_event'
23
Taking false branch
497 current_event = NULL__null;
498
499
500 // Here we should unlink the furthest_event and delete it.
501 // Also decrease the current undo-depth by one
502 if (furthest_event
23.1
'furthest_event' is not equal to NULL
!= NULL__null) {
24
Taking true branch
503 if (furthest_event->next_event != NULL__null)
25
Assuming field 'next_event' is equal to NULL
26
Taking false branch
504 furthest_event->next_event->previous_event = furthest_event->previous_event;
505 if (furthest_event->previous_event
26.1
Field 'previous_event' is equal to NULL
!= NULL__null)
27
Taking false branch
506 furthest_event->previous_event->next_event = furthest_event->next_event;
507
508 furthest_event->next_event = NULL__null;
509 furthest_event->previous_event = NULL__null;
510
511 delete furthest_event;
28
Memory is released
512 }
513 current_queue_depth--;
514 }
515 }
516
517 if (maximum_queue_depth == 0) {
518 // Delete everything.
519 for (int32 i=0;i<layer_bitmap_count;i++) {
520 delete layer_bitmaps[i];
521 layer_bitmaps[i] = NULL__null;
522 }
523 delete[] layer_bitmaps;
524 layer_bitmap_count = 0;
525 current_queue_depth = 0;
526 layer_bitmaps = NULL__null;
527 }
528
529 UpdateMenuItems();
530}
531
532
533void
534UndoQueue::SetSelectionData(const SelectionData *s)
535{
536 delete selection_data;
537 selection_data = new SelectionData(s);
538}