Bug Summary

File:home/HaikuArchives/ArtPaint/artpaint/application/UndoQueue.cpp
Warning:line 485, column 12
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-07-02-122529-1240-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
426 && ((depth < maximum_queue_depth)
2
Assuming 'depth' is < 'maximum_queue_depth'
427 || (maximum_queue_depth == INFINITE_QUEUE_DEPTH-1))) {
428 maximum_queue_depth = depth;
429 for (int32 i = 0; i < queue_list->CountItems(); i++) {
3
Assuming the condition is true
4
Loop condition is true. Entering loop body
430 UndoQueue *queue = (UndoQueue*)queue_list->ItemAt(i);
431 queue->TruncateQueue();
5
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)) {
6
Assuming 'maximum_queue_depth' is < field 'current_queue_depth'
7
Taking true branch
454 while (current_queue_depth > maximum_queue_depth
7.1
'maximum_queue_depth' is < field 'current_queue_depth'
) {
8
Loop condition is true. Entering loop body
26
Assuming 'maximum_queue_depth' is < field 'current_queue_depth'
27
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
27.1
Field 'current_event' is equal to NULL
!= NULL__null
) {
9
Assuming field 'current_event' is equal to NULL
10
Taking false branch
28
Taking false branch
464 UndoEvent *redo_direction=current_event;
465 UndoEvent *undo_direction=current_event;
466 while ((undo_direction != NULL__null) && (redo_direction != NULL__null)) {
467 undo_direction = undo_direction->previous_event;
468 redo_direction = redo_direction->next_event;
469 }
470 if (undo_direction != NULL__null) {
471 while (undo_direction->previous_event != NULL__null)
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
28.1
Field 'first_event' is not equal to NULL
!= NULL__null
) {
11
Assuming field 'first_event' is not equal to NULL
12
Taking true branch
29
Taking true branch
484 furthest_event = first_event;
485 while (furthest_event->next_event != NULL__null)
13
Assuming field 'next_event' is not equal to NULL
14
Loop condition is true. Entering loop body
15
Assuming field 'next_event' is equal to NULL
16
Loop condition is false. Execution continues on line 491
30
Use of memory after it is freed
486 furthest_event = furthest_event->next_event;
487 }
488 else
489 current_queue_depth = 0;
490
491 if (furthest_event == first_event) {
17
Assuming 'furthest_event' is equal to field 'first_event'
18
Taking true branch
492 if (first_event
18.1
Field 'first_event' is not equal to NULL
!= NULL__null)
19
Taking true branch
493 first_event = first_event->next_event;
494 }
495
496 if (current_event == furthest_event
19.1
'furthest_event' is not equal to field 'current_event'
)
20
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
20.1
'furthest_event' is not equal to NULL
!= NULL__null) {
21
Taking true branch
503 if (furthest_event->next_event
21.1
Field 'next_event' is equal to NULL
!= NULL__null)
22
Taking false branch
504 furthest_event->next_event->previous_event = furthest_event->previous_event;
505 if (furthest_event->previous_event != NULL__null)
23
Assuming field 'previous_event' is equal to NULL
24
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;
25
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}