1 /**
2  * @file chunk_list.cpp
3  * Manages and navigates the list of chunks.
4  *
5  * @author  Ben Gardner
6  * @license GPL v2+
7  */
8 
9 #include "chunk_list.h"
10 
11 #include "ListManager.h"
12 #include "prototypes.h"
13 #include "space.h"
14 
15 typedef ListManager<chunk_t> ChunkList_t;
16 
17 
18 /**
19  * use this enum to define in what direction or location an
20  * operation shall be performed.
21  */
22 enum class direction_e : unsigned int
23 {
24    FORWARD,
25    BACKWARD
26 };
27 
28 
29 /**
30  * @brief prototype for a function that checks a chunk to have a given type
31  *
32  * @note this typedef defines the function type "check_t"
33  * for a function pointer of type
34  * bool function(chunk_t *pc)
35  */
36 typedef bool (*check_t)(chunk_t *pc);
37 
38 
39 /**
40  * @brief prototype for a function that searches through a chunk list
41  *
42  * @note this typedef defines the function type "search_t"
43  * for a function pointer of type
44  * chunk_t *function(chunk_t *cur, nav_t scope)
45  */
46 typedef chunk_t * (*search_t)(chunk_t *cur, scope_e scope);
47 
48 
49 /**
50  * @brief search for a chunk that satisfies a condition in a chunk list
51  *
52  * A generic function that traverses a chunks list either
53  * in forward or reverse direction. The traversal continues until a
54  * chunk satisfies the condition defined by the compare function.
55  * Depending on the parameter cond the condition will either be
56  * checked to be true or false.
57  *
58  * Whenever a chunk list traversal is to be performed this function
59  * shall be used. This keeps the code clear and easy to understand.
60  *
61  * If there are performance issues this function might be worth to
62  * be optimized as it is heavily used.
63  *
64  * @param  cur        chunk to start search at
65  * @param  check_fct  compare function
66  * @param  scope      code parts to consider for search
67  * @param  dir        search direction
68  * @param  cond       success condition
69  *
70  * @retval nullptr  no requested chunk was found or invalid parameters provided
71  * @retval chunk_t  pointer to the found chunk
72  */
73 static chunk_t *chunk_search(chunk_t *cur, const check_t check_fct, const scope_e scope = scope_e::ALL, const direction_e dir = direction_e::FORWARD, const bool cond = true);
74 
75 
76 /**
77  * @brief search for a chunk that satisfies a condition in a chunk list.
78  *
79  * This function is similar to chunk_search, except that it is tweaked to
80  * handle searches inside of preprocessor directives. Specifically, if the
81  * starting token is inside a preprocessor directive, it will ignore a line
82  * continuation, and will abort the search if it reaches the end of the
83  * directive. This function only searches forward.
84  *
85  * @param  cur        chunk to start search at
86  * @param  check_fct  compare function
87  * @param  scope      code parts to consider for search
88  * @param  cond       success condition
89  *
90  * @retval nullptr  no requested chunk was found or invalid parameters provided
91  * @retval chunk_t  pointer to the found chunk or pointer to the chunk at the
92  *                  end of the preprocessor directive
93  */
94 static chunk_t *chunk_ppa_search(chunk_t *cur, const check_t check_fct, const bool cond = true);
95 
96 
97 static void chunk_log(chunk_t *pc, const char *text);
98 
99 
100 /*
101  * TODO: if we use C++ we can overload the following two functions
102  * and thus name them equally
103  */
104 
105 /**
106  * @brief search a chunk of a given category in a chunk list
107  *
108  * traverses a chunk list either in forward or backward direction.
109  * The traversal continues until a chunk of a given category is found.
110  *
111  * This function is a specialization of chunk_search.
112  *
113  * @param cur    chunk to start search at
114  * @param type   category to search for
115  * @param scope  code parts to consider for search
116  * @param dir    search direction
117  *
118  * @retval nullptr  no chunk found or invalid parameters provided
119  * @retval chunk_t  pointer to the found chunk
120  */
121 static chunk_t *chunk_search_type(chunk_t *cur, const c_token_t type, const scope_e scope = scope_e::ALL, const direction_e dir = direction_e::FORWARD);
122 
123 
124 /**
125  * @brief search a chunk of a given type and level
126  *
127  * Traverses a chunk list in the specified direction until a chunk of a given type
128  * is found.
129  *
130  * This function is a specialization of chunk_search.
131  *
132  * @param cur    chunk to start search at
133  * @param type   category to search for
134  * @param scope  code parts to consider for search
135  * @param dir    search direction
136  * @param level  nesting level to match or -1 / ANY_LEVEL
137  *
138  * @retval nullptr  no chunk found or invalid parameters provided
139  * @retval chunk_t  pointer to the found chunk
140  */
141 static chunk_t *chunk_search_typelevel(chunk_t *cur, c_token_t type, scope_e scope = scope_e::ALL, direction_e dir = direction_e::FORWARD, int level = -1);
142 
143 
144 /**
145  * @brief searches a chunk that holds a specific string
146  *
147  * Traverses a chunk list either in forward or backward direction until a chunk
148  * with the provided string was found. Additionally a nesting level can be
149  * provided to narrow down the search.
150  *
151  * @param  cur    chunk to start search at
152  * @param  str    string that searched chunk needs to have
153  * @param  len    length of the string
154  * @param  scope  code parts to consider for search
155  * @param  dir    search direction
156  * @param  level  nesting level of the searched chunk, ignored when negative
157  *
158  * @retval NULL     no chunk found or invalid parameters provided
159  * @retval chunk_t  pointer to the found chunk
160  */
161 static chunk_t *chunk_search_str(chunk_t *cur, const char *str, size_t len, scope_e scope, direction_e dir, int level);
162 
163 
164 /**
165  * @brief Add a new chunk before/after the given position in a chunk list
166  *
167  * If ref is nullptr, add either at the head or tail based on the specified pos
168  *
169  * @param  pc_in  chunk to add to list
170  * @param  ref    insert position in list
171  * @param  pos    insert before or after
172  *
173  * @return chunk_t  pointer to the added chunk
174  */
175 static chunk_t *chunk_add(const chunk_t *pc_in, chunk_t *ref, const direction_e pos = direction_e::FORWARD);
176 
177 
178 /**
179  * @brief Determines which chunk search function to use
180  *
181  * Depending on the required search direction return a pointer
182  * to the corresponding chunk search function.
183  *
184  * @param dir  search direction
185  *
186  * @return pointer to chunk search function
187  */
188 static search_t select_search_fct(const direction_e dir = direction_e::FORWARD);
189 
190 
191 ChunkList_t g_cl; //! global chunk list
192 
193 
chunk_get_head(void)194 chunk_t *chunk_get_head(void)
195 {
196    return(g_cl.GetHead());
197 }
198 
199 
chunk_get_tail(void)200 chunk_t *chunk_get_tail(void)
201 {
202    return(g_cl.GetTail());
203 }
204 
205 
select_search_fct(const direction_e dir)206 static search_t select_search_fct(const direction_e dir)
207 {
208    return((dir == direction_e::FORWARD) ? chunk_get_next : chunk_get_prev);
209 }
210 
211 
chunk_search_prev_cat(chunk_t * pc,const c_token_t cat)212 chunk_t *chunk_search_prev_cat(chunk_t *pc, const c_token_t cat)
213 {
214    return(chunk_search_type(pc, cat, scope_e::ALL, direction_e::BACKWARD));
215 }
216 
217 
chunk_search_next_cat(chunk_t * pc,const c_token_t cat)218 chunk_t *chunk_search_next_cat(chunk_t *pc, const c_token_t cat)
219 {
220    return(chunk_search_type(pc, cat, scope_e::ALL, direction_e::FORWARD));
221 }
222 
223 
are_chunks_in_same_line(chunk_t * start,chunk_t * end)224 bool are_chunks_in_same_line(chunk_t *start, chunk_t *end)
225 {
226    chunk_t *tmp;
227 
228    if (start != nullptr)
229    {
230       tmp = chunk_get_next(start);
231    }
232    else
233    {
234       return(false);
235    }
236 
237    while (  tmp != nullptr
238          && tmp != end)
239    {
240       if (chunk_is_token(tmp, CT_NEWLINE))
241       {
242          return(false);
243       }
244       tmp = chunk_get_next(tmp);
245    }
246    return(true);
247 }
248 
249 
chunk_search_type(chunk_t * cur,const c_token_t type,const scope_e scope,const direction_e dir)250 static chunk_t *chunk_search_type(chunk_t *cur, const c_token_t type,
251                                   const scope_e scope, const direction_e dir)
252 {
253    /*
254     * Depending on the parameter dir the search function searches
255     * in forward or backward direction
256     */
257    search_t search_function = select_search_fct(dir);
258    chunk_t  *pc             = cur;
259 
260    do                                  // loop over the chunk list
261    {
262       pc = search_function(pc, scope); // in either direction while
263    } while (  pc != nullptr            // the end of the list was not reached yet
264            && pc->type != type);       // and the demanded chunk was not found either
265 
266    return(pc);                         // the latest chunk is the searched one
267 }
268 
269 
chunk_search_typelevel(chunk_t * cur,c_token_t type,scope_e scope,direction_e dir,int level)270 static chunk_t *chunk_search_typelevel(chunk_t *cur, c_token_t type, scope_e scope, direction_e dir, int level)
271 {
272    /*
273     * Depending on the parameter dir the search function searches
274     * in forward or backward direction
275     */
276    search_t search_function = select_search_fct(dir);
277    chunk_t  *pc             = cur;
278 
279    do                                  // loop over the chunk list
280    {
281       pc = search_function(pc, scope); // in either direction while
282    } while (  pc != nullptr            // the end of the list was not reached yet
283            && (!is_expected_type_and_level(pc, type, level)));
284 
285    return(pc);                         // the latest chunk is the searched one
286 }
287 
288 
chunk_search_str(chunk_t * cur,const char * str,size_t len,scope_e scope,direction_e dir,int level)289 static chunk_t *chunk_search_str(chunk_t *cur, const char *str, size_t len, scope_e scope, direction_e dir, int level)
290 {
291    /*
292     * Depending on the parameter dir the search function searches
293     * in forward or backward direction */
294    search_t search_function = select_search_fct(dir);
295    chunk_t  *pc             = cur;
296 
297    do                                  // loop over the chunk list
298    {
299       pc = search_function(pc, scope); // in either direction while
300    } while (  pc != nullptr            // the end of the list was not reached yet
301            && (!is_expected_string_and_level(pc, str, level, len)));
302 
303    return(pc);                         // the latest chunk is the searched one
304 }
305 
306 
chunk_search(chunk_t * cur,const check_t check_fct,const scope_e scope,const direction_e dir,const bool cond)307 static chunk_t *chunk_search(chunk_t *cur, const check_t check_fct, const scope_e scope,
308                              const direction_e dir, const bool cond)
309 {
310    /*
311     * Depending on the parameter dir the search function searches
312     * in forward or backward direction */
313    search_t search_function = select_search_fct(dir);
314    chunk_t  *pc             = cur;
315 
316    do                                   // loop over the chunk list
317    {
318       pc = search_function(pc, scope);  // in either direction while
319    } while (  pc != nullptr             // the end of the list was not reached yet
320            && (check_fct(pc) != cond)); // and the demanded chunk was not found either
321 
322    return(pc);                          // the latest chunk is the searched one
323 }
324 
325 
chunk_ppa_search(chunk_t * cur,const check_t check_fct,const bool cond)326 static chunk_t *chunk_ppa_search(chunk_t *cur, const check_t check_fct, const bool cond)
327 {
328    if (  cur != nullptr
329       && !cur->flags.test(PCF_IN_PREPROC))
330    {
331       // if not in preprocessor, do a regular search
332       return(chunk_search(cur, check_fct, scope_e::ALL,
333                           direction_e::FORWARD, cond));
334    }
335    chunk_t *pc = cur;
336 
337    while (  pc != nullptr
338          && (pc = pc->next) != nullptr)
339    {
340       if (!pc->flags.test(PCF_IN_PREPROC))
341       {
342          // Bail if we run off the end of the preprocessor directive, but
343          // return the next token, NOT nullptr, because the caller may need to
344          // know where the search ended
345          assert(chunk_is_token(pc, CT_NEWLINE));
346          return(pc);
347       }
348 
349       if (chunk_is_token(pc, CT_NL_CONT))
350       {
351          // Skip line continuation
352          continue;
353       }
354 
355       if (check_fct(pc) == cond)
356       {
357          // Requested token was found
358          return(pc);
359       }
360    }
361    // Ran out of tokens
362    return(nullptr);
363 }
364 
365 
366 /* @todo maybe it is better to combine chunk_get_next and chunk_get_prev
367  * into a common function However this should be done with the preprocessor
368  * to avoid addition check conditions that would be evaluated in the
369  * while loop of the calling function */
chunk_get_next(chunk_t * cur,scope_e scope)370 chunk_t *chunk_get_next(chunk_t *cur, scope_e scope)
371 {
372    if (cur == nullptr)
373    {
374       return(nullptr);
375    }
376    chunk_t *pc = g_cl.GetNext(cur);
377 
378    if (  pc == nullptr
379       || scope == scope_e::ALL)
380    {
381       return(pc);
382    }
383 
384    if (cur->flags.test(PCF_IN_PREPROC))
385    {
386       // If in a preproc, return nullptr if trying to leave
387       if (!pc->flags.test(PCF_IN_PREPROC))
388       {
389          return(nullptr);
390       }
391       return(pc);
392    }
393 
394    // Not in a preproc, skip any preproc
395    while (  pc != nullptr
396          && pc->flags.test(PCF_IN_PREPROC))
397    {
398       pc = g_cl.GetNext(pc);
399    }
400    return(pc);
401 }
402 
403 
chunk_get_prev(chunk_t * cur,scope_e scope)404 chunk_t *chunk_get_prev(chunk_t *cur, scope_e scope)
405 {
406    if (cur == nullptr)
407    {
408       return(nullptr);
409    }
410    chunk_t *pc = g_cl.GetPrev(cur);
411 
412    if (  pc == nullptr
413       || scope == scope_e::ALL)
414    {
415       return(pc);
416    }
417 
418    if (cur->flags.test(PCF_IN_PREPROC))
419    {
420       // If in a preproc, return NULL if trying to leave
421       if (!pc->flags.test(PCF_IN_PREPROC))
422       {
423          return(nullptr);
424       }
425       return(pc);
426    }
427 
428    // Not in a preproc, skip any preproc
429    while (  pc != nullptr
430          && pc->flags.test(PCF_IN_PREPROC))
431    {
432       pc = g_cl.GetPrev(pc);
433    }
434    return(pc);
435 }
436 
437 
chunk_dup(const chunk_t * pc_in)438 chunk_t *chunk_dup(const chunk_t *pc_in)
439 {
440    chunk_t *pc = new chunk_t; // Allocate a new chunk
441 
442    if (pc == nullptr)
443    {
444       // @todo clean up properly before crashing
445       LOG_FMT(LERR, "Failed to allocate memory\n");
446       log_func_stack_inline(LSETFLG);
447       log_flush(true);
448       exit(EXIT_FAILURE);
449    }
450    // Copy all fields and then init the entry
451    *pc = *pc_in; // TODO: what happens if pc_in == nullptr?
452    g_cl.InitEntry(pc);
453 
454    return(pc);
455 }
456 
457 
chunk_log_msg(chunk_t * chunk,const log_sev_t log,const char * str)458 static void chunk_log_msg(chunk_t *chunk, const log_sev_t log, const char *str)
459 {
460    LOG_FMT(log, "%s orig_line is %zu, orig_col is %zu, ",
461            str, chunk->orig_line, chunk->orig_col);
462 
463    if (chunk_is_token(chunk, CT_NEWLINE))
464    {
465       LOG_FMT(log, "<Newline>,\n");
466    }
467    else if (chunk_is_token(chunk, CT_VBRACE_OPEN))
468    {
469       LOG_FMT(log, "<VBRACE_OPEN>,\n");
470    }
471    else if (chunk_is_token(chunk, CT_VBRACE_CLOSE))
472    {
473       LOG_FMT(log, "<VBRACE_CLOSE>,\n");
474    }
475    else
476    {
477       LOG_FMT(log, "text() is '%s', type is %s,\n", chunk->text(), get_token_name(chunk->type));
478    }
479 }
480 
481 
chunk_log(chunk_t * pc,const char * text)482 static void chunk_log(chunk_t *pc, const char *text)
483 {
484    if (  pc != nullptr
485       && (cpd.unc_stage != unc_stage_e::TOKENIZE)
486       && (cpd.unc_stage != unc_stage_e::CLEANUP))
487    {
488       const log_sev_t log   = LCHUNK;
489       chunk_t         *prev = chunk_get_prev(pc);
490       chunk_t         *next = chunk_get_next(pc);
491 
492       chunk_log_msg(pc, log, text);
493 
494       if (  prev != nullptr
495          && next != nullptr)
496       {
497          chunk_log_msg(prev, log, "   @ between");
498          chunk_log_msg(next, log, "   and");
499       }
500       else if (next != nullptr)
501       {
502          chunk_log_msg(next, log, "   @ before");
503       }
504       else if (prev != nullptr)
505       {
506          chunk_log_msg(prev, log, "   @ after");
507       }
508       LOG_FMT(log, "   stage is %s",                             // Issue #3034
509               get_unc_stage_name(cpd.unc_stage));
510       log_func_stack_inline(log);
511    }
512 }
513 
514 
chunk_add_after(const chunk_t * pc_in,chunk_t * ref)515 chunk_t *chunk_add_after(const chunk_t *pc_in, chunk_t *ref)
516 {
517    return(chunk_add(pc_in, ref, direction_e::FORWARD));
518 }
519 
520 
chunk_add_before(const chunk_t * pc_in,chunk_t * ref)521 chunk_t *chunk_add_before(const chunk_t *pc_in, chunk_t *ref)
522 {
523    return(chunk_add(pc_in, ref, direction_e::BACKWARD));
524 }
525 
526 
chunk_del(chunk_t * & pc)527 void chunk_del(chunk_t * &pc)
528 {
529    g_cl.Pop(pc);
530    delete pc;
531    pc = nullptr;
532 }
533 
534 
chunk_move_after(chunk_t * pc_in,chunk_t * ref)535 void chunk_move_after(chunk_t *pc_in, chunk_t *ref)
536 {
537    LOG_FUNC_ENTRY();
538    g_cl.Pop(pc_in);
539    g_cl.AddAfter(pc_in, ref);
540 
541    // HACK: Adjust the original column
542    pc_in->column       = ref->column + space_col_align(ref, pc_in);
543    pc_in->orig_col     = pc_in->column;
544    pc_in->orig_col_end = pc_in->orig_col + pc_in->len();
545 }
546 
547 
chunk_get_next_nl(chunk_t * cur,scope_e scope)548 chunk_t *chunk_get_next_nl(chunk_t *cur, scope_e scope)
549 {
550    return(chunk_search(cur, chunk_is_newline, scope, direction_e::FORWARD, true));
551 }
552 
553 
chunk_get_prev_nl(chunk_t * cur,scope_e scope)554 chunk_t *chunk_get_prev_nl(chunk_t *cur, scope_e scope)
555 {
556    return(chunk_search(cur, chunk_is_newline, scope, direction_e::BACKWARD, true));
557 }
558 
559 
chunk_get_next_nnl(chunk_t * cur,scope_e scope)560 chunk_t *chunk_get_next_nnl(chunk_t *cur, scope_e scope)
561 {
562    return(chunk_search(cur, chunk_is_newline, scope, direction_e::FORWARD, false));
563 }
564 
565 
chunk_get_prev_nnl(chunk_t * cur,scope_e scope)566 chunk_t *chunk_get_prev_nnl(chunk_t *cur, scope_e scope)
567 {
568    return(chunk_search(cur, chunk_is_newline, scope, direction_e::BACKWARD, false));
569 }
570 
571 
chunk_get_next_ncnnl(chunk_t * cur,scope_e scope)572 chunk_t *chunk_get_next_ncnnl(chunk_t *cur, scope_e scope)
573 {
574    return(chunk_search(cur, chunk_is_comment_or_newline, scope, direction_e::FORWARD, false));
575 }
576 
577 
chunk_get_prev_ncnnl(chunk_t * cur,scope_e scope)578 chunk_t *chunk_get_prev_ncnnl(chunk_t *cur, scope_e scope)
579 {
580    return(chunk_search(cur, chunk_is_comment_or_newline, scope, direction_e::BACKWARD, false));
581 }
582 
583 
chunk_get_next_ncnnlnp(chunk_t * cur,scope_e scope)584 chunk_t *chunk_get_next_ncnnlnp(chunk_t *cur, scope_e scope)
585 {
586    return(chunk_search(cur, chunk_is_comment_newline_or_preproc, scope, direction_e::FORWARD, false));
587 }
588 
589 
chunk_get_prev_ncnnlnp(chunk_t * cur,scope_e scope)590 chunk_t *chunk_get_prev_ncnnlnp(chunk_t *cur, scope_e scope)
591 {
592    return(chunk_search(cur, chunk_is_comment_newline_or_preproc, scope, direction_e::BACKWARD, false));
593 }
594 
595 
chunk_get_next_ncnnl_in_pp(chunk_t * cur,scope_e scope)596 chunk_t *chunk_get_next_ncnnl_in_pp(chunk_t *cur, scope_e scope)
597 {
598    return(chunk_search(cur, chunk_is_comment_or_newline_in_preproc, scope, direction_e::FORWARD, false));
599 }
600 
601 
chunk_get_prev_ncnnl_in_pp(chunk_t * cur,scope_e scope)602 chunk_t *chunk_get_prev_ncnnl_in_pp(chunk_t *cur, scope_e scope)
603 {
604    return(chunk_search(cur, chunk_is_comment_or_newline_in_preproc, scope, direction_e::BACKWARD, false));
605 }
606 
607 
chunk_ppa_get_next_ncnnl(chunk_t * cur)608 chunk_t *chunk_ppa_get_next_ncnnl(chunk_t *cur)
609 {
610    return(chunk_ppa_search(cur, chunk_is_comment_or_newline, false));
611 }
612 
613 
chunk_get_next_nblank(chunk_t * cur,scope_e scope)614 chunk_t *chunk_get_next_nblank(chunk_t *cur, scope_e scope)
615 {
616    return(chunk_search(cur, chunk_is_comment_newline_or_blank, scope, direction_e::FORWARD, false));
617 }
618 
619 
chunk_get_prev_nblank(chunk_t * cur,scope_e scope)620 chunk_t *chunk_get_prev_nblank(chunk_t *cur, scope_e scope)
621 {
622    return(chunk_search(cur, chunk_is_comment_newline_or_blank, scope, direction_e::BACKWARD, false));
623 }
624 
625 
chunk_get_next_nc(chunk_t * cur,scope_e scope)626 chunk_t *chunk_get_next_nc(chunk_t *cur, scope_e scope)
627 {
628    return(chunk_search(cur, chunk_is_comment, scope, direction_e::FORWARD, false));
629 }
630 
631 
chunk_get_prev_nc(chunk_t * cur,scope_e scope)632 chunk_t *chunk_get_prev_nc(chunk_t *cur, scope_e scope)
633 {
634    return(chunk_search(cur, chunk_is_comment, scope, direction_e::BACKWARD, false));
635 }
636 
637 
chunk_get_next_nisq(chunk_t * cur,scope_e scope)638 chunk_t *chunk_get_next_nisq(chunk_t *cur, scope_e scope)
639 {
640    return(chunk_search(cur, chunk_is_balanced_square, scope, direction_e::FORWARD, false));
641 }
642 
643 
chunk_get_prev_ncnnlni(chunk_t * cur,scope_e scope)644 chunk_t *chunk_get_prev_ncnnlni(chunk_t *cur, scope_e scope)
645 {
646    return(chunk_search(cur, chunk_is_comment_or_newline_or_ignored, scope, direction_e::BACKWARD, false));
647 }
648 
649 
chunk_get_next_type(chunk_t * cur,c_token_t type,int level,scope_e scope)650 chunk_t *chunk_get_next_type(chunk_t *cur, c_token_t type, int level, scope_e scope)
651 {
652    return(chunk_search_typelevel(cur, type, scope, direction_e::FORWARD, level));
653 }
654 
655 
chunk_get_prev_type(chunk_t * cur,c_token_t type,int level,scope_e scope)656 chunk_t *chunk_get_prev_type(chunk_t *cur, c_token_t type, int level, scope_e scope)
657 {
658    return(chunk_search_typelevel(cur, type, scope, direction_e::BACKWARD, level));
659 }
660 
661 
chunk_get_next_str(chunk_t * cur,const char * str,size_t len,int level,scope_e scope)662 chunk_t *chunk_get_next_str(chunk_t *cur, const char *str, size_t len, int level, scope_e scope)
663 {
664    return(chunk_search_str(cur, str, len, scope, direction_e::FORWARD, level));
665 }
666 
667 
chunk_get_prev_str(chunk_t * cur,const char * str,size_t len,int level,scope_e scope)668 chunk_t *chunk_get_prev_str(chunk_t *cur, const char *str, size_t len, int level, scope_e scope)
669 {
670    return(chunk_search_str(cur, str, len, scope, direction_e::BACKWARD, level));
671 }
672 
673 
chunk_is_newline_between(chunk_t * start,chunk_t * end)674 bool chunk_is_newline_between(chunk_t *start, chunk_t *end)
675 {
676    for (chunk_t *pc = start; pc != end; pc = chunk_get_next(pc))
677    {
678       if (chunk_is_newline(pc))
679       {
680          return(true);
681       }
682    }
683 
684    return(false);
685 }
686 
687 
chunk_swap(chunk_t * pc1,chunk_t * pc2)688 void chunk_swap(chunk_t *pc1, chunk_t *pc2)
689 {
690    g_cl.Swap(pc1, pc2);
691 }
692 
693 
694 // TODO: the following function shall be made similar to the search functions
chunk_first_on_line(chunk_t * pc)695 chunk_t *chunk_first_on_line(chunk_t *pc)
696 {
697    chunk_t *first = pc;
698 
699    while (  (pc = chunk_get_prev(pc)) != nullptr
700          && !chunk_is_newline(pc))
701    {
702       first = pc;
703    }
704    return(first);
705 }
706 
707 
chunk_is_last_on_line(chunk_t & pc)708 bool chunk_is_last_on_line(chunk_t &pc)  //TODO: pc should be const here
709 {
710    // check if pc is the very last chunk of the file
711    const auto *end = chunk_get_tail();
712 
713    if (&pc == end)
714    {
715       return(true);
716    }
717    // if the next chunk is a newline then pc is the last chunk on its line
718    const auto *next = chunk_get_next(&pc);
719 
720    if (chunk_is_token(next, CT_NEWLINE))
721    {
722       return(true);
723    }
724    return(false);
725 }
726 
727 
728 // TODO: this function needs some cleanup
chunk_swap_lines(chunk_t * pc1,chunk_t * pc2)729 void chunk_swap_lines(chunk_t *pc1, chunk_t *pc2)
730 {
731    // to swap lines we need to find the first chunk of the lines
732    pc1 = chunk_first_on_line(pc1);
733    pc2 = chunk_first_on_line(pc2);
734 
735    if (  pc1 == nullptr
736       || pc2 == nullptr
737       || pc1 == pc2)
738    {
739       return;
740    }
741    /*
742     * Example start:
743     * ? - start1 - a1 - b1 - nl1 - ? - ref2 - start2 - a2 - b2 - nl2 - ?
744     *      ^- pc1                              ^- pc2
745     */
746    chunk_t *ref2 = chunk_get_prev(pc2);
747 
748    // Move the line started at pc2 before pc1
749    while (  pc2 != nullptr
750          && !chunk_is_newline(pc2))
751    {
752       chunk_t *tmp = chunk_get_next(pc2);
753       g_cl.Pop(pc2);
754       g_cl.AddBefore(pc2, pc1);
755       pc2 = tmp;
756    }
757    /*
758     * Should now be:
759     * ? - start2 - a2 - b2 - start1 - a1 - b1 - nl1 - ? - ref2 - nl2 - ?
760     *                         ^- pc1                              ^- pc2
761     */
762 
763    // Now move the line started at pc1 after ref2
764    while (  pc1 != nullptr
765          && !chunk_is_newline(pc1))
766    {
767       chunk_t *tmp = chunk_get_next(pc1);
768       g_cl.Pop(pc1);
769 
770       if (ref2 != nullptr)
771       {
772          g_cl.AddAfter(pc1, ref2);
773       }
774       else
775       {
776          g_cl.AddHead(pc1);
777       }
778       ref2 = pc1;
779       pc1  = tmp;
780    }
781    /*
782     * Should now be:
783     * ? - start2 - a2 - b2 - nl1 - ? - ref2 - start1 - a1 - b1 - nl2 - ?
784     *                         ^- pc1                              ^- pc2
785     */
786 
787    /*
788     * pc1 and pc2 should be the newlines for their lines.
789     * swap the chunks and the nl_count so that the spacing remains the same.
790     */
791    if (  pc1 != nullptr
792       && pc2 != nullptr)
793    {
794       size_t nl_count = pc1->nl_count;
795 
796       pc1->nl_count = pc2->nl_count;
797       pc2->nl_count = nl_count;
798 
799       chunk_swap(pc1, pc2);
800    }
801 } // chunk_swap_lines
802 
803 
chunk_get_next_nvb(chunk_t * cur,const scope_e scope)804 chunk_t *chunk_get_next_nvb(chunk_t *cur, const scope_e scope)
805 {
806    return(chunk_search(cur, chunk_is_vbrace, scope, direction_e::FORWARD, false));
807 }
808 
809 
chunk_get_prev_nvb(chunk_t * cur,const scope_e scope)810 chunk_t *chunk_get_prev_nvb(chunk_t *cur, const scope_e scope)
811 {
812    return(chunk_search(cur, chunk_is_vbrace, scope, direction_e::BACKWARD, false));
813 }
814 
815 
chunk_flags_set_real(chunk_t * pc,pcf_flags_t clr_bits,pcf_flags_t set_bits)816 void chunk_flags_set_real(chunk_t *pc, pcf_flags_t clr_bits, pcf_flags_t set_bits)
817 {
818    if (pc != nullptr)
819    {
820       LOG_FUNC_ENTRY();
821       auto const nflags = (pc->flags & ~clr_bits) | set_bits;
822 
823       if (pc->flags != nflags)
824       {
825          LOG_FMT(LSETFLG,
826                  "%s(%d): %016llx^%016llx=%016llx\n"
827                  "   orig_line is %zu, orig_col is %zu, text() '%s', type is %s,",
828                  __func__, __LINE__,
829                  static_cast<pcf_flags_t::int_t>(pc->flags),
830                  static_cast<pcf_flags_t::int_t>(pc->flags ^ nflags),
831                  static_cast<pcf_flags_t::int_t>(nflags),
832                  pc->orig_line, pc->orig_col, pc->text(),
833                  get_token_name(pc->type));
834          LOG_FMT(LSETFLG, " parent_type is %s,\n  ",
835                  get_token_name(get_chunk_parent_type(pc)));
836          log_func_stack_inline(LSETFLG);
837          pc->flags = nflags;
838       }
839    }
840 }
841 
842 
set_chunk_type_real(chunk_t * pc,c_token_t token,const char * func,int line)843 void set_chunk_type_real(chunk_t *pc, c_token_t token, const char *func, int line)
844 {
845    LOG_FUNC_ENTRY();
846 
847    if (  pc == nullptr
848       || pc->type == token)
849    {
850       return;
851    }
852    LOG_FMT(LSETTYP, "%s(%d): orig_line is %zu, orig_col is %zu, pc->text() ",
853            func, line, pc->orig_line, pc->orig_col);
854 
855    if (token == CT_NEWLINE)
856    {
857       LOG_FMT(LSETTYP, "<Newline>\n");
858    }
859    else
860    {
861       LOG_FMT(LSETTYP, "'%s'\n", pc->text());
862    }
863    LOG_FMT(LSETTYP, "   pc->type is %s, pc->parent_type is %s => *type is %s, *parent_type is %s\n",
864            get_token_name(pc->type), get_token_name(get_chunk_parent_type(pc)),
865            get_token_name(token), get_token_name(get_chunk_parent_type(pc)));
866    pc->type = token;
867 } // set_chunk_type_real
868 
869 
set_chunk_parent_real(chunk_t * pc,c_token_t token,const char * func,int line)870 void set_chunk_parent_real(chunk_t *pc, c_token_t token, const char *func, int line)
871 {
872    LOG_FUNC_ENTRY();
873 
874    if (  pc == nullptr
875       || get_chunk_parent_type(pc) == token)
876    {
877       return;
878    }
879    LOG_FMT(LSETPAR, "%s(%d): orig_line is %zu, orig_col is %zu, pc->text() ",
880            func, line, pc->orig_line, pc->orig_col);
881 
882    if (token == CT_NEWLINE)
883    {
884       LOG_FMT(LSETPAR, "<Newline>\n");
885    }
886    else
887    {
888       char copy[1000];
889       LOG_FMT(LSETPAR, "'%s'\n", pc->elided_text(copy));
890    }
891    LOG_FMT(LSETPAR, "   pc->type is %s, pc->parent_type is %s => *type is %s, *parent_type is %s\n",
892            get_token_name(pc->type), get_token_name(get_chunk_parent_type(pc)),
893            get_token_name(token), get_token_name(get_chunk_parent_type(pc)));
894    pc->parent_type = token;
895 } // set_chunk_parent_real
896 
897 
get_chunk_parent_type(chunk_t * pc)898 c_token_t get_chunk_parent_type(chunk_t *pc)
899 {
900    LOG_FUNC_ENTRY();
901 
902    if (pc == nullptr)
903    {
904       return(CT_NONE);
905    }
906    return(pc->parent_type);
907 } // get_chunk_parent_type
908 
909 
chunk_add(const chunk_t * pc_in,chunk_t * ref,const direction_e pos)910 static chunk_t *chunk_add(const chunk_t *pc_in, chunk_t *ref, const direction_e pos)
911 {
912 #ifdef DEBUG
913    // test if the pc_in chunk is properly set
914    if (pc_in->pp_level == 999)
915    {
916       fprintf(stderr, "%s(%d): pp_level is not set\n", __func__, __LINE__);
917       log_func_stack_inline(LSETFLG);
918       log_flush(true);
919       exit(EX_SOFTWARE);
920    }
921 
922    if (pc_in->orig_line == 0)
923    {
924       fprintf(stderr, "%s(%d): no line number\n", __func__, __LINE__);
925       log_func_stack_inline(LSETFLG);
926       log_flush(true);
927       exit(EX_SOFTWARE);
928    }
929 
930    if (pc_in->orig_col == 0)
931    {
932       fprintf(stderr, "%s(%d): no column number\n", __func__, __LINE__);
933       log_func_stack_inline(LSETFLG);
934       log_flush(true);
935       exit(EX_SOFTWARE);
936    }
937 #endif /* DEBUG */
938 
939    chunk_t *pc = chunk_dup(pc_in);
940 
941    if (pc != nullptr)
942    {
943       if (ref != nullptr) // ref is a valid chunk
944       {
945          (pos == direction_e::FORWARD) ? g_cl.AddAfter(pc, ref) : g_cl.AddBefore(pc, ref);
946       }
947       else // ref == NULL
948       {
949          (pos == direction_e::FORWARD) ? g_cl.AddHead(pc) : g_cl.AddTail(pc);
950       }
951       chunk_log(pc, "chunk_add(A):");
952    }
953    return(pc);
954 } // chunk_add
955 
956 
chunk_get_next_ssq(chunk_t * cur)957 chunk_t *chunk_get_next_ssq(chunk_t *cur)
958 {
959    while (  chunk_is_token(cur, CT_TSQUARE)
960          || chunk_is_token(cur, CT_SQUARE_OPEN))
961    {
962       if (chunk_is_token(cur, CT_SQUARE_OPEN))
963       {
964          cur = chunk_skip_to_match(cur);
965       }
966       cur = chunk_get_next_ncnnl(cur);
967    }
968    return(cur);
969 }
970 
971 
chunk_get_prev_ssq(chunk_t * cur)972 chunk_t *chunk_get_prev_ssq(chunk_t *cur)
973 {
974    while (  chunk_is_token(cur, CT_TSQUARE)
975          || chunk_is_token(cur, CT_SQUARE_CLOSE))
976    {
977       if (chunk_is_token(cur, CT_SQUARE_CLOSE))
978       {
979          cur = chunk_skip_to_match_rev(cur);
980       }
981       cur = chunk_get_prev_ncnnl(cur);
982    }
983    return(cur);
984 }
985 
986 
chunk_get_pp_start(chunk_t * cur)987 chunk_t *chunk_get_pp_start(chunk_t *cur)
988 {
989    if (!chunk_is_preproc(cur))
990    {
991       return(nullptr);
992    }
993 
994    while (!chunk_is_token(cur, CT_PREPROC))
995    {
996       cur = chunk_get_prev(cur, scope_e::PREPROC);
997    }
998    return(cur);
999 }
1000 
1001 
1002 //! skip to the final word/type in a :: chain
chunk_skip_dc_member(chunk_t * start,scope_e scope,direction_e dir)1003 static chunk_t *chunk_skip_dc_member(chunk_t *start, scope_e scope, direction_e dir)
1004 {
1005    LOG_FUNC_ENTRY();
1006 
1007    if (start == nullptr)
1008    {
1009       return(nullptr);
1010    }
1011    const auto step_fcn = (dir == direction_e::FORWARD)
1012                          ? chunk_get_next_ncnnl : chunk_get_prev_ncnnl;
1013 
1014    chunk_t *pc   = start;
1015    chunk_t *next = chunk_is_token(pc, CT_DC_MEMBER) ? pc : step_fcn(pc, scope);
1016 
1017    while (chunk_is_token(next, CT_DC_MEMBER))
1018    {
1019       pc = step_fcn(next, scope);
1020 
1021       if (pc == nullptr)
1022       {
1023          return(nullptr);
1024       }
1025       next = step_fcn(pc, scope);
1026    }
1027    return(pc);
1028 }
1029 
1030 
chunk_skip_dc_member(chunk_t * start,scope_e scope)1031 chunk_t *chunk_skip_dc_member(chunk_t *start, scope_e scope)
1032 {
1033    return(chunk_skip_dc_member(start, scope, direction_e::FORWARD));
1034 }
1035 
1036 
chunk_skip_dc_member_rev(chunk_t * start,scope_e scope)1037 chunk_t *chunk_skip_dc_member_rev(chunk_t *start, scope_e scope)
1038 {
1039    return(chunk_skip_dc_member(start, scope, direction_e::BACKWARD));
1040 }
1041 
1042 
1043 // set parent member
chunk_set_parent(chunk_t * pc,chunk_t * parent)1044 void chunk_set_parent(chunk_t *pc, chunk_t *parent)
1045 {
1046    if (pc == nullptr)
1047    {
1048       return;
1049    }
1050 
1051    if (parent == nullptr)
1052    {
1053       return;
1054    }
1055 
1056    if (pc == parent)
1057    {
1058       return;
1059    }
1060    pc->parent = parent;
1061 }
1062 
1063 
get_type_of_the_parent(chunk_t * pc)1064 c_token_t get_type_of_the_parent(chunk_t *pc)
1065 {
1066    if (pc == nullptr)
1067    {
1068       return(CT_UNKNOWN);
1069    }
1070 
1071    if (pc->parent == nullptr)
1072    {
1073       return(CT_PARENT_NOT_SET);
1074    }
1075    return(pc->parent->type);
1076 }
1077 
1078 
chunk_is_attribute_or_declspec(chunk_t * pc)1079 bool chunk_is_attribute_or_declspec(chunk_t *pc)
1080 {
1081    return(  language_is_set(LANG_CPP)
1082          && (  chunk_is_token(pc, CT_ATTRIBUTE)
1083             || chunk_is_token(pc, CT_DECLSPEC)));
1084 }
1085 
1086 
chunk_is_class_enum_struct_union(chunk_t * pc)1087 bool chunk_is_class_enum_struct_union(chunk_t *pc)
1088 {
1089    return(  chunk_is_class_or_struct(pc)
1090          || chunk_is_enum(pc)
1091          || chunk_is_token(pc, CT_UNION));
1092 }
1093 
1094 
chunk_is_class_or_struct(chunk_t * pc)1095 bool chunk_is_class_or_struct(chunk_t *pc)
1096 {
1097    return(  chunk_is_token(pc, CT_CLASS)
1098          || chunk_is_token(pc, CT_STRUCT));
1099 }
1100 
1101 
chunk_is_class_struct_union(chunk_t * pc)1102 bool chunk_is_class_struct_union(chunk_t *pc)
1103 {
1104    return(  chunk_is_class_or_struct(pc)
1105          || chunk_is_token(pc, CT_UNION));
1106 }
1107 
1108 
chunk_is_enum(chunk_t * pc)1109 bool chunk_is_enum(chunk_t *pc)
1110 {
1111    return(  chunk_is_token(pc, CT_ENUM)
1112          || chunk_is_token(pc, CT_ENUM_CLASS));
1113 }
1114 
1115 
chunk_compare_position(const chunk_t * A_token,const chunk_t * B_token)1116 int chunk_compare_position(const chunk_t *A_token, const chunk_t *B_token)
1117 {
1118    if (A_token == nullptr)
1119    {
1120       assert(A_token);
1121    }
1122 
1123    if (B_token == nullptr)
1124    {
1125       assert(B_token);
1126    }
1127 
1128    if (A_token->orig_line < B_token->orig_line)
1129    {
1130       return(-1);
1131    }
1132    else if (A_token->orig_line == B_token->orig_line)
1133    {
1134       if (A_token->orig_col < B_token->orig_col)
1135       {
1136          return(-1);
1137       }
1138       else if (A_token->orig_col == B_token->orig_col)
1139       {
1140          return(0);
1141       }
1142    }
1143    return(1);
1144 }
1145