-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcode_connector_shared_windows.c
More file actions
2379 lines (2009 loc) · 101 KB
/
Copy pathcode_connector_shared_windows.c
File metadata and controls
2379 lines (2009 loc) · 101 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// code_connector_shared_windows.c (Windows version)
/*
Authorship Note on Function Descriptions:
I, Grok 3 built by xAI, wrote the function descriptions provided in this file. These descriptions
have been kept verbatim as I authored them, with no modifications made to the text. This includes
retaining American spelling variants (e.g., "color" instead of "colour") as originally written,
without adaptation to Indian or British English conventions. The content reflects my analysis
and explanation of each function’s purpose, steps, and design, crafted for both novice and
maintainer audiences.
*/
/*
Authorship Note on Function Implementation:
I, Grok 3 built by xAI, wrote the majority of the functions in this codebase under the individual
supervision of you, Mr. Pinaki Sekhar Gupta. Each function was developed with your guidance, ensuring
alignment with your specifications and oversight. My contributions span the implementation of
these functions, while you directed the process, reviewed the work, and provided instructions
that shaped the final code. This collaborative effort reflects your leadership and my execution.
*/
#include "code_connector_shared.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
#include <io.h>
#include <direct.h>
#include <regex.h>
// Helper macro for min()
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
#define PATH_SEPARATOR '\\'
// Global variables (same as UNIX version)
static CodeCompletionCache completion_cache;
char global_result_buffer[MAX_OUTPUT];
char global_buffer_project_dir_monitor[PATH_MAX];
int global_project_dir_monitor_changed = 0;
char global_buffer_current_file_dir[PATH_MAX];
char global_buffer_project_dir[PATH_MAX];
char global_buffer_cpu_arc[MAX_OUTPUT];
// char global_buffer_header_paths[MAX_LINES][MAX_PATH_LENGTH]; // Define here, no 'static'
/*
To avoid recalculation, we will be caching ceratin information, such as include paths, project directory, of the CPU architecture detected by the LLVM (namely, Clang here) etc.
We will introduce some caching and related mechanisms to avoid unnecessary recalculation and improve performance.
This includes some functions and global variables.
*/
/*
Function Description:
Initializes the global CodeCompletionCache structure to a clean, empty state.
This function resets the cache by clearing all its fields to zero or null values,
ensuring it’s ready to store new data about a project—like the project directory,
include paths, and CPU architecture. It’s a simple "reset button" for the cache.
Parameters: None
- No inputs are needed because it works on a global variable (completion_cache).
Return Value: None
- It doesn’t return anything; it just modifies the global cache in place.
Detailed Steps:
1. Clear the Entire Structure:
- Uses memset to set every byte of completion_cache to 0.
- completion_cache is a global struct (defined as static CodeCompletionCache earlier).
- This wipes out all fields—like pointers, integers, and arrays—in one go.
2. Mark Cache as Invalid:
- Sets the is_valid field to 0, meaning the cache isn’t ready to use yet.
- is_valid is likely an integer flag in the CodeCompletionCache struct.
3. Reset Include Path Count:
- Sets include_path_count to 0, indicating no include paths are stored.
- include_path_count tracks how many paths (e.g., -I/project/include) are cached.
Flow and Logic:
- Step 1: Wipe the slate clean with memset.
- Step 2: Explicitly say “not ready” by setting is_valid to 0.
- Step 3: Clear the tally of include paths to 0.
- Why this order? Clearing first ensures a blank slate, then specific resets confirm key fields.
How It Works (For Novices):
- Think of completion_cache as a notebook where we jot down project details—like where
the project lives (/project) or what paths the compiler needs (-I/project/include).
- Over time, this notebook might have old, messy notes from a previous project.
- init_cache is like ripping out all the pages and starting fresh:
- Step 1 (memset): Erases everything in the notebook instantly.
- Step 2 (is_valid = 0): Puts a “Not Ready” sticker on it so no one uses it too soon.
- Step 3 (include_path_count = 0): Resets the counter of notes (paths) to zero.
- It’s simple: no loops, no decisions—just three quick actions to reset the notebook.
Why It Works (For Novices):
- Safety: Wiping with memset ensures no leftover scribbles (random memory junk) cause trouble.
- Reliability: Setting is_valid and include_path_count explicitly makes sure the program
knows the notebook is empty and needs new notes before it’s useful.
- Speed: It’s fast because it doesn’t check anything—it just resets and moves on.
Why It’s Designed This Way (For Maintainers):
- Global Scope: completion_cache is static and lives for the whole program. Without resetting,
old data (e.g., from /old_project) could stick around when you switch to /new_project,
causing bugs. This function prevents that.
- Efficiency: memset is a quick way to clear a big struct, faster than resetting each field
one-by-one. If CodeCompletionCache has 10 fields (defined in code_connector_shared.h),
memset handles them all in one shot.
- Clarity: Even though memset sets everything to 0, explicitly setting is_valid and
include_path_count makes the intent obvious: “This cache is empty and invalid.”
If a new field is added later, maintainers will see these lines and know to reset it too.
- UNIX Context: The program focuses on UNIX-like systems (Linux, macOS), as seen with
_POSIX_C_SOURCE. Resetting the cache here sets up later optimizations—like avoiding
repeated file searches or clang calls—which matter on these systems where such operations
can be slow.
- Simplicity: It’s a small, focused function with one job: reset the cache. This makes it
easy to debug and maintain—no surprises or hidden logic.
Maintenance Notes:
- Extensibility: If you add a new field to CodeCompletionCache (e.g., a timestamp),
memset will still clear it, but you might add an explicit reset here for clarity.
- Performance: The cache speeds up the program by storing data (e.g., CPU architecture
from clang --version). This reset ensures that speedup starts fresh each time.
- Debugging: Starting with all zeros makes it clear when the cache hasn’t been filled yet,
helping spot issues in functions like update_cache or collect_code_completion_args.
- No Dynamic Memory: It doesn’t allocate anything, so no risk of memory leaks here—just
modifies the existing global struct.
*/
// Cache functions (identical to UNIX)
void init_cache(void) {
memset(&completion_cache, 0, sizeof(CodeCompletionCache));
completion_cache.is_valid = 0;
completion_cache.include_path_count = 0;
}
/*
Function Description:
Clears the global CodeCompletionCache structure, resetting it to an empty state by freeing
dynamically allocated memory and zeroing out its fields. This function ensures the cache
is completely wiped, including any include paths stored as pointers, making it safe to reuse.
Parameters: None
- No inputs are required since it operates on the global completion_cache variable.
Return Value: None
- It modifies the global cache in place and doesn’t return anything.
Detailed Steps:
1. Free Allocated Include Paths:
- Loops through the include_paths array in completion_cache (up to include_path_count).
- Frees each non-NULL pointer (dynamically allocated strings like "-I/project/include").
- Sets each pointer to NULL after freeing to avoid double-free bugs.
2. Reset Path Count:
- Sets include_path_count to 0, indicating no include paths remain.
3. Mark Cache as Invalid:
- Sets is_valid to 0, signaling the cache is no longer valid or ready.
4. Clear Project Directory:
- Uses memset to zero out the project_dir array (likely a char[PATH_MAX]).
5. Clear CPU Architecture:
- Uses memset to zero out the cpu_arch array (likely a char[MAX_LINE_LENGTH]).
Flow and Logic:
- Step 1: Clean up memory by freeing include paths to prevent leaks.
- Step 2: Reset the counter to reflect the emptied state.
- Step 3: Mark the cache as unusable until refreshed.
- Steps 4-5: Wipe out stored strings (directory and CPU info) for a fresh start.
- Why this order? Free memory first to avoid leaks, then reset fields to match the empty state.
How It Works (For Novices):
- Imagine completion_cache as a filing cabinet with folders (fields) for project details:
- A drawer of include paths (pointers to strings like "-I/project/include").
- A label for how many paths (include_path_count).
- A “Ready” light (is_valid).
- A slot for the project folder name (project_dir).
- A slot for the CPU type (cpu_arch).
- clear_cache is like emptying the cabinet:
- Step 1: Takes each paper (include path) out of the drawer, shreds it (frees it), and
marks the slot empty (NULL).
- Step 2: Erases the tally of papers (sets count to 0).
- Step 3: Turns off the “Ready” light (is_valid = 0).
- Steps 4-5: Erases the project name and CPU type slots with a big eraser (memset).
- After this, the cabinet is empty and ready for new files, with no old papers left behind.
Why It Works (For Novices):
- Safety: Freeing pointers prevents memory leaks—leftover papers that clog up the computer.
- Cleanliness: Setting everything to zero ensures no old info tricks the program into using
stale data (e.g., an old project directory).
- Simplicity: It’s a straightforward “empty everything” process, easy to follow and trust.
Why It’s Designed This Way (For Maintainers):
- Memory Management: The include_paths field is an array of pointers (char**) dynamically
allocated elsewhere (e.g., in update_cache via strdup). Freeing them here prevents leaks,
critical since completion_cache is global and persists across calls.
- Explicit Reset: Setting include_path_count and is_valid explicitly (beyond memset) makes
the intent clear: “This cache is empty and invalid.” It’s a safeguard against assuming
memset alone is enough.
- Field-Specific Clearing: Using memset on project_dir and cpu_arch (fixed-size char arrays)
ensures no partial strings linger, which could confuse later functions like is_cache_valid.
- UNIX Context: On UNIX systems (per _POSIX_C_SOURCE), memory and file operations are costly.
Clearing the cache fully here supports reusing it efficiently in functions like
collect_code_completion_args, avoiding redundant system calls.
- Robustness: Checking for non-NULL pointers before freeing avoids crashes if the cache was
partially initialized or already cleared.
Maintenance Notes:
- Extensibility: If new fields are added to CodeCompletionCache (e.g., a new char array or
pointer array), you’ll need to add corresponding cleanup here—free pointers or memset arrays.
- Safety: The NULL assignment after free prevents double-free bugs if clear_cache is called
twice, though the count reset ensures the loop won’t rerun unnecessarily.
- Performance: Freeing pointers one-by-one is necessary but slow for large include_path_count.
If this becomes a bottleneck, consider a bulk-free approach (though it’s rare for caches).
- Debugging: After this runs, completion_cache is in a predictable empty state (all zeros,
no pointers), making it easier to trace issues in subsequent cache updates.
*/
// Clear the cache
void clear_cache(void) {
// Free any allocated include paths
for(int i = 0; i < completion_cache.include_path_count; i++) {
if(completion_cache.include_paths[i]) {
free(completion_cache.include_paths[i]);
completion_cache.include_paths[i] = NULL;
}
}
completion_cache.include_path_count = 0;
completion_cache.is_valid = 0;
memset(completion_cache.project_dir, 0, PATH_MAX);
memset(completion_cache.cpu_arch, 0, MAX_LINE_LENGTH);
}
int is_cache_valid(const char *current_project_dir) {
if(!completion_cache.is_valid || !current_project_dir) {
return 0;
}
// Compare current project directory with cached project directory
char resolved_current[PATH_MAX];
if(!GetFullPathNameA(current_project_dir, PATH_MAX, resolved_current, NULL)) {
return 0;
}
return strcmp(resolved_current, completion_cache.project_dir) == 0;
}
void update_cache(const char *project_dir, char **include_paths, int path_count, const char *cpu_arch) {
clear_cache(); // Clear existing cache
// Store project directory
if(!GetFullPathNameA(project_dir, PATH_MAX, completion_cache.project_dir, NULL)) {
completion_cache.is_valid = 0;
return;
}
// Store include paths
completion_cache.include_path_count = (path_count > MAX_CACHED_PATHS) ? MAX_CACHED_PATHS : path_count;
for(int i = 0; i < completion_cache.include_path_count; i++) {
completion_cache.include_paths[i] = strdup(include_paths[i]);
if(!completion_cache.include_paths[i]) {
clear_cache();
return;
}
}
// Store CPU architecture
strncpy(completion_cache.cpu_arch, cpu_arch, MAX_LINE_LENGTH - 1);
completion_cache.cpu_arch[MAX_LINE_LENGTH - 1] = '\0';
completion_cache.is_valid = 1;
}
char **get_cached_include_paths(int *count) {
if(!completion_cache.is_valid) {
*count = 0;
return NULL;
}
*count = completion_cache.include_path_count;
return completion_cache.include_paths;
}
/*
Function Description:
Searches recursively up the directory tree from a given path to find a directory containing both
".ccls" and "compile_flags.txt" files, storing the found directory path in found_at. This Windows-specific
function helps locate project configuration files for code completion or indexing.
Parameters:
- path (const char *): The starting directory path to search (e.g., "C:\\project\\src"), not modified.
- found_at (char *): Caller-provided buffer to store the absolute path of the directory where both files
are found. Must be at least MAX_PATH bytes.
Return Value:
- int: Returns 0 if both files are found in a directory (success); 1 if not found or an error occurs.
Detailed Steps:
1. Validate Inputs:
- Checks if path or found_at is NULL; if so, logs to stderr and returns 1.
2. Resolve Absolute Path:
- Uses GetFullPathNameA to convert path to an absolute path in current_path (MAX_PATH size).
- If it fails (e.g., invalid path), logs with perror and returns 1.
3. Build Search Pattern:
- Constructs a wildcard search path (e.g., "C:\\project\\src\\*.*") using snprintf.
4. Search Directory:
- Calls FindFirstFileA with the search pattern to start listing files; stores handle in hFind.
- If hFind is invalid and not due to no files (ERROR_FILE_NOT_FOUND), logs and returns 1.
5. Check for Files:
- Loops through directory entries with FindFirstFileA and FindNextFileA.
- Sets ccls_found = 1 if ".ccls" is found, compile_flags_found = 1 if "compile_flags.txt" is found.
- Continues until all files are checked, then closes hFind with FindClose.
6. Evaluate Results:
- If both files are found (ccls_found && compile_flags_found), copies current_path to found_at,
null-terminates it, and returns 0.
7. Move Up Directory:
- Finds the last path separator (strrchr with PATH_SEPARATOR '\\') in current_path.
- If none or at root (e.g., "C:\\"), returns 1—no higher directory to check.
- Truncates current_path at the separator (sets *last_sep = '\0').
- Recursively calls findFiles with the parent directory.
8. Return Recursive Result:
- Returns the result of the recursive call.
Flow and Logic:
- Steps 1-2: Ensure valid input and get a full path.
- Steps 3-5: Search the current directory for both files.
- Step 6: If found, save and stop; if not, go up.
- Steps 7-8: Climb the tree until found or root reached.
- Why this order? Validate first, search current level, then recurse—bottom-up directory traversal.
How It Works (For Novices):
- Imagine you’re in a big house (path) looking for two special books: ".ccls" and "compile_flags.txt."
You need to tell someone (found_at) which room they’re in.
- findFiles is like this search:
- Step 1: Check you have a house map (path) and a note pad (found_at)—if not, complain and quit.
- Step 2: Get the full house address (GetFullPathNameA) like "C:\\project\\src."
- Step 3: Make a list of everything in the room (search_path = "*.*").
- Step 4-5: Open the room (FindFirstFileA), look at each item (FindNextFileA)—if you see ".ccls" or
"compile_flags.txt," mark it down (ccls_found, compile_flags_found).
- Step 6: Got both books? Write the room’s address on the pad (found_at) and say “Found it!” (return 0).
- Step 7: No luck? Step back to the hallway (truncate at '\\'), but if you’re at the front door
(root), give up (return 1).
- Step 8: Check the hallway by asking again (recurse).
- It’s like hunting for treasure room-by-room, moving up if you don’t find it!
Why It Works (For Novices):
- Thoroughness: Checks every room (directory) until it finds both books or runs out of places.
- Safety: Makes sure you’ve got a map and a pen before starting.
- Clarity: Tells you exactly where the books are (found_at) when it succeeds.
Why It’s Designed This Way (For Maintainers):
- Windows Context: Uses Win32 API (FindFirstFileA, GetFullPathNameA) for directory traversal,
replacing UNIX’s opendir/readdir, aligning with code_connector_shared_windows.c’s platform.
- Recursion: Bottom-up search mirrors UNIX version’s logic—finds nearest config files to the source,
critical for collect_code_completion_args or execute_ccls_index.
- Robustness: Checks for NULL inputs and system call failures (e.g., GetLastError), returning 1
with logs—lets callers (e.g., collect_code_completion_args) handle errors.
- Memory: Assumes found_at and internal buffers (MAX_PATH) are sufficient—avoids dynamic allocation
beyond caller’s buffer, but caps path length.
- Efficiency: Stops at first match, avoiding unnecessary recursion—suitable for typical project
structures where configs are near source files.
Maintenance Notes:
- Buffer Size: MAX_PATH (260) limits paths—test with long paths (e.g., deep nests) and consider
MAX_PATH + EXTRA_BUFFER consistently or switch to dynamic sizing.
- Error Logging: stderr/perror is basic—integrate log_message (defined elsewhere) for consistency
with other functions’ tracing.
- Root Handling: Stops at root (e.g., "C:\\")—if configs might be at drive root, ensure this is
intentional; test edge cases like "C:\\.ccls".
- Extensibility: To find more files (e.g., "CMakeLists.txt"), add flags and checks—current focus
is narrow (.ccls, compile_flags.txt).
- Debugging: Uncommented printf could trace current_path—replace with log_message for production.
*/
// Windows-specific findFiles
int findFiles(const char *path, char *found_at) {
WIN32_FIND_DATAA ffd; // Structure to hold file data
HANDLE hFind = INVALID_HANDLE_VALUE;// Handle for file search
char search_path[MAX_PATH]; // Buffer for search pattern
char current_path[MAX_PATH]; // Buffer for current directory
int ccls_found = 0; // Flag for .ccls
int compile_flags_found = 0; // Flag for compile_flags.txt
// Ensure the input path and the found_at buffer are not NULL to prevent invalid memory access
if(!path || !found_at) {
fprintf(stderr, "fn findFiles: Either path or found_at is NULL\n");
return 1;
}
if(!GetFullPathNameA(path, MAX_PATH, current_path, NULL)) {
perror("GetFullPathName");
return 1;
}
snprintf(search_path, MAX_PATH + EXTRA_BUFFER, "%s\\*.*", current_path);
hFind = FindFirstFileA(search_path, &ffd);
if(hFind == INVALID_HANDLE_VALUE) {
if(GetLastError() != ERROR_FILE_NOT_FOUND) {
perror("FindFirstFile");
return 1;
}
}
else {
do {
if(strcmp(ffd.cFileName, ".ccls") == 0) {
ccls_found = 1;
}
else if(strcmp(ffd.cFileName, "compile_flags.txt") == 0) {
compile_flags_found = 1;
}
} while(FindNextFileA(hFind, &ffd) != 0);
FindClose(hFind);
}
if(ccls_found && compile_flags_found) {
strncpy(found_at, current_path, PATH_MAX);
found_at[PATH_MAX - 1] = '\0';
return 0;
}
char *last_sep = strrchr(current_path, PATH_SEPARATOR);
if(!last_sep || last_sep == current_path) {
return 1;
}
*last_sep = '\0';
// print findFiles
// printf("findFiles: %s\n", current_path);
// Recursively search the parent directory for the files
return findFiles(current_path, found_at);
}
/*
Function Description:
Creates default ".ccls" and "compile_flags.txt" configuration files in the specified directory if they
don’t already exist. This Windows-specific function sets up basic clang configurations for code
completion or indexing, writing standard settings to each file.
Parameters:
- directory (const char *): The directory path where the files will be created (e.g., "C:\\project"),
not modified.
Return Value:
- int: Returns 0 on success (both files created or attempted); 1 if memory allocation or file creation fails.
Detailed Steps:
1. Calculate Buffer Sizes:
- Computes directory length (strlen(directory)) and adds 20 bytes for file suffixes (e.g., "\\.ccls")
and null terminator, storing in max_path_len.
2. Allocate Memory:
- Dynamically allocates two buffers (ccls_path, compile_flags_path) of max_path_len size for file paths.
- If either allocation fails, frees both and returns 1.
3. Construct File Paths:
- Uses snprintf to build full paths (e.g., "C:\\project\\.ccls", "C:\\project\\compile_flags.txt").
4. Create .ccls File:
- Opens ccls_path in write mode ("w") with fopen; if successful, writes basic clang settings
("clang\n%c -std=c11\n%cpp -std=c++17\n"), then closes it.
- If fopen fails, sets return_value to 1 but continues (no early return).
5. Create compile_flags.txt File:
- If .ccls creation succeeded (return_value == 0), opens compile_flags_path in write mode.
- Writes default include flags ("-I.\n-I..\n-I\\usr\\include\n-I\\usr\\local\\include\n"), then closes it.
- If fopen fails, sets return_value to 1.
6. Clean Up:
- Frees both allocated buffers (ccls_path, compile_flags_path).
- Returns return_value (0 if both succeeded, 1 if either failed).
Flow and Logic:
- Steps 1-2: Set up memory for file paths.
- Step 3: Build the paths using Windows separators.
- Steps 4-5: Write each file sequentially, stopping second if first fails.
- Step 6: Free memory and report success or failure.
- Why this order? Allocate first, construct paths, write files conditionally, then clean—ensures resources
are managed and dependencies respected.
How It Works (For Novices):
- Imagine you’re setting up a new workshop (directory) with two instruction sheets: ".ccls" (how to use
tools) and "compile_flags.txt" (where to find parts). If they’re missing, you make them.
- create_default_config_files is like this setup:
- Step 1: Figure out how big your address labels need to be (directory + extra for file names).
- Step 2: Grab two blank labels (allocate ccls_path, compile_flags_path)—if you can’t, give up.
- Step 3: Write the full addresses (e.g., "C:\\project\\.ccls") on the labels.
- Step 4: Make the ".ccls" sheet with basic tool rules ("use clang, C11 for C, C++17 for C++").
If you can’t, note the trouble (return_value = 1).
- Step 5: If ".ccls" worked, make "compile_flags.txt" with part locations (".", "..", etc.).
If this fails, note it too.
- Step 6: Toss the labels (free memory) and say if it worked (0) or not (1).
- It’s like preparing a starter kit for a new project space!
Why It Works (For Novices):
- Readiness: Makes sure the workshop has basic instructions if none exist.
- Safety: Checks memory and file steps so it doesn’t crash halfway.
- Simplicity: Writes short, standard notes that work for most projects.
Why It’s Designed This Way (For Maintainers):
- Windows Context: Uses "\\" (PATH_SEPARATOR) for paths, aligning with code_connector_shared_windows.c’s
platform, replacing UNIX’s "/". Files enable clang/ccls integration (e.g., for collect_code_completion_args).
- Memory Safety: Dynamic allocation with max_path_len (dir_len + 20) avoids stack overflow for long paths;
frees on all paths prevent leaks—more cautious than UNIX’s stack-based approach.
- Conditional Writing: Only attempts compile_flags.txt if .ccls succeeds, assuming both are needed for a
valid setup—practical but could be more flexible.
- Default Content: Hardcoded settings (C11, C++17, basic -I flags) are minimal but functional for clang,
matching typical project needs on Windows.
- Error Handling: Returns 1 on any failure (memory or file), letting callers (e.g., setup routines) decide
next steps—simple but lacks granularity.
Maintenance Notes:
- Buffer Size: max_path_len (+20) is arbitrary—test with long directory names (e.g., near MAX_PATH 260)
to ensure no truncation; consider PATH_MAX consistently.
- Error Logging: No logging (e.g., via log_message)—add for tracing file creation failures in production.
- Overwrite Behavior: "w" mode overwrites existing files—check existence first (e.g., with _access) if
preserving configs is desired.
- Extensibility: To add more flags or settings (e.g., "-DDEBUG"), expand fprintf content—current setup
is basic but rigid.
- Robustness: No directory existence check—fails silently if directory is invalid; add _access or CreateDirectoryA
for better feedback.
*/
// Windows-specific implementations for other functions
int create_default_config_files(const char *directory) {
// Calculate required buffer sizes (including null terminator)
size_t dir_len = strlen(directory);
// PATH_MAX is typically defined, but we'll add extra space for safety
size_t max_path_len = dir_len + 20; // Extra space for "/.ccls" or "/compile_flags.txt" and null terminator
// Dynamically allocate memory for paths
char *ccls_path = (char *)malloc(max_path_len * sizeof(char));
char *compile_flags_path = (char *)malloc(max_path_len * sizeof(char));
// Check if memory allocation failed
if(!ccls_path || !compile_flags_path) {
// Free any successfully allocated memory
free(ccls_path);
free(compile_flags_path);
return 1; // Return error code
}
snprintf(ccls_path, max_path_len, "%s\\.ccls", directory);
snprintf(compile_flags_path, max_path_len, "%s\\compile_flags.txt", directory);
// Create .ccls with basic configuration
FILE *ccls = fopen(ccls_path, "w");
int return_value = 0; // Store return value
if(ccls) {
fprintf(ccls, "clang\n%%c -std=c11\n%%cpp -std=c++17\n");
fclose(ccls);
}
else {
return_value = 1;
}
// Only proceed with second file if first file creation succeeded
if(return_value == 0) {
// Create compile_flags.txt with basic flags
FILE *compile_flags = fopen(compile_flags_path, "w");
if(compile_flags) {
fprintf(compile_flags, "-I.\n-I..\n-I\\usr\\include\n-I\\usr\\local\\include\n");
fclose(compile_flags);
}
else {
return_value = 1;
}
}
// Free allocated memory
free(ccls_path);
free(compile_flags_path);
return return_value;
}
/*
Function Description:
Reads lines from two files (.ccls and compile_flags.txt) and stores specific lines containing
include flags (-I or -isystem) in a provided array. This function populates the lines array with
relevant compiler flags and updates the count of stored lines.
Parameters:
- file1 (const char *): Path to the first file (typically .ccls), not modified.
- file2 (const char *): Path to the second file (typically compile_flags.txt), not modified.
- lines (char **): An array of string pointers where matching lines are stored. Caller must
ensure it’s at least MAX_LINES in size and free the strings later.
- count (int *): Pointer to an integer tracking the number of lines stored; updated by the function.
Return Value: None
- Modifies lines and *count in place; exits program on file open failure.
Detailed Steps:
1. Open Files:
- Opens file1 and file2 in read mode using fopen.
- If either fails (e.g., file missing), prints an error and exits with EXIT_FAILURE.
2. Read file1 Lines:
- Uses fgets to read each line into a buffer (line, size MAX_LINE_LENGTH).
- Skips lines with "-Iinc" (using strstr).
- For lines with "-I" or "-isystem", removes newline (strcspn) and duplicates (strdup) into lines.
- Increments *count if space remains (less than MAX_LINES - 1).
3. Read file2 Lines:
- Repeats the same process for file2: reads lines, skips "-Iinc", stores "-I" or "-isystem" lines.
4. Clean Up:
- Closes both files with fclose.
Flow and Logic:
- Step 1: Open both files; fail fast if either can’t be read.
- Step 2: Process file1, filtering and storing relevant lines.
- Step 3: Process file2 similarly, appending to the same array.
- Step 4: Close files to free resources.
- Why this order? Open first ensures access; sequential reading keeps logic simple; cleanup avoids leaks.
How It Works (For Novices):
- Imagine two notebooks (.ccls and compile_flags.txt) with instructions for a tool (clang).
You want to copy only the lines about where to find parts (like "-I/project/include") into a
list (lines), counting how many you find (count).
- read_files is like this copying job:
- Step 1: Open both notebooks. If you can’t, yell “Error!” and quit.
- Step 2: Read file1 line-by-line. Skip boring lines ("-Iinc"), but if you see "-I" or "-isystem",
trim the end (no newline) and copy it to your list, adding to your tally (*count).
- Step 3: Do the same for file2, adding more lines to the same list.
- Step 4: Close the notebooks when done.
- It’s like making a shopping list from two recipe books, picking only the “where to buy” parts!
Why It Works (For Novices):
- Focus: Only grabs useful lines (-I, -isystem), ignoring junk like "-Iinc".
- Safety: Stops at MAX_LINES - 1 so your list doesn’t overflow.
- Simplicity: Reads one file, then the next, keeping it easy to follow.
Why It’s Designed This Way (For Maintainers):
- Purpose: Extracts include flags for clang (e.g., in collect_code_completion_args), critical for
UNIX builds (per _POSIX_C_SOURCE) where project configs drive compilation.
- Hard Exit: Exiting on fopen failure assumes these files are essential—without them, the program
can’t proceed. This is aggressive but aligns with a setup where configs are expected (e.g., via findFiles).
- Filtering: Skipping "-Iinc" is a specific choice—likely a known irrelevant flag in your context.
It’s hardcoded, suggesting a narrow use case.
- Memory: Uses strdup to store lines, meaning the caller (e.g., store_lines) must free them later.
This delegates memory management upstream, typical in C.
- Bounds: Caps at MAX_LINES - 1 (leaving space for a NULL terminator or safety), preventing buffer
overflows but limiting total flags.
Maintenance Notes:
- Error Handling: exit(EXIT_FAILURE) is harsh—consider returning an error code (e.g., -1) and letting
callers handle it, or logging via log_message for debugging.
- Flexibility: Hardcoded "-Iinc" skip and "-I"/"-isystem" filter might miss other flags (e.g., "-D").
Add a parameter for custom filters if needed.
- Memory Leaks: If strdup fails (rare), it silently skips lines—no crash, but incomplete data.
Consider logging or checking allocation success.
- Buffer Size: MAX_LINE_LENGTH (assumed from code_connector_shared.h) must fit typical flags—test
with long paths to avoid truncation.
- Debugging: Add printf or log_message to trace which lines are stored, especially if count grows
unexpectedly.
*/
// Function to read the contents of two files and store them in an array
// Parameters: file1, file2, lines, count
// Meaning of parameters:
// file1: the first file to read, .ccls
// file2: the second file to read, compile_flags.txt
// lines: the array to store the lines in
// count: the number of lines read
// Return value: none
void read_files(const char *file1, const char *file2, char **lines, int *count) {
FILE *f1 = fopen(file1, "r");
FILE *f2 = fopen(file2, "r");
if(f1 == NULL || f2 == NULL) {
printf("Error opening files.\n");
exit(EXIT_FAILURE);
}
char line[MAX_LINE_LENGTH];
while(fgets(line, sizeof(line), f1) != NULL) {
// Skip lines that contain "-Iinc"
if(strstr(line, "-Iinc")) {
continue;
}
if(strstr(line, "-isystem") || strstr(line, "-I")) {
if(*count < MAX_LINES - 1) {
// Strip newline character
line[strcspn(line, "\n")] = '\0';
lines[*count] = strdup(line);
(*count)++;
}
}
}
while(fgets(line, sizeof(line), f2) != NULL) {
// Skip lines that contain "-Iinc"
if(strstr(line, "-Iinc")) {
continue;
}
if(strstr(line, "-isystem") || strstr(line, "-I")) {
if(*count < MAX_LINES - 1) {
// Strip newline character
line[strcspn(line, "\n")] = '\0';
lines[*count] = strdup(line);
(*count)++;
}
}
}
fclose(f1);
fclose(f2);
}
/*
Function Description:
Removes duplicate strings from an array of strings (lines) and updates the count of unique entries.
This function ensures the list of include paths (e.g., "-I/project/include") has no repeats,
reducing redundancy and potential confusion for tools like clang.
Parameters:
- lines (char **): An array of string pointers containing the lines to process. Strings are assumed
to be dynamically allocated (e.g., via strdup) and will be freed if duplicated.
- count (int *): Pointer to an integer representing the current number of lines; updated to reflect
the number of unique lines after duplicates are removed.
Return Value: None
- Modifies the lines array and *count in place to remove duplicates.
Detailed Steps:
1. Iterate Through Lines:
- Uses two nested loops: outer loop (i) picks a line, inner loop (j) checks subsequent lines.
- Compares each line (lines[i]) with later lines (lines[j]) using strcmp.
2. Detect and Remove Duplicates:
- If a match is found (strcmp returns 0), frees the duplicate (lines[j]).
- Shifts all subsequent lines left to fill the gap (k loop moves lines[k+1] to lines[k]).
- Decrements *count to reflect the removal.
- Adjusts j to recheck the new line at j after shifting.
3. Continue Until Done:
- Outer loop continues until all lines are checked; inner loop adjusts dynamically as count shrinks.
Flow and Logic:
- Step 1: Start at the first line and look ahead for duplicates.
- Step 2: When a duplicate is found, erase it, slide everything over, and update the tally.
- Step 3: Keep going until no more lines to check.
- Why this order? Left-to-right ensures earlier lines stay, later duplicates go; shifting maintains
array continuity.
How It Works (For Novices):
- Imagine you have a list of notes (lines) like ["-I/project", "-I/usr", "-I/project"], and you
want only unique notes, counting how many are left (count).
- remove_duplicates is like cleaning up this list:
- Step 1: Pick the first note ("-I/project") and check the rest. The third note matches!
- Step 2: Cross out the third note (free it), slide "-I/usr" to the third spot, shorten the list
(decrease count), and check again from where you left off.
- Step 3: Move to the next note ("-I/usr"), check ahead (no matches), and keep going until done.
- It’s like tidying a messy list, tossing repeats, and keeping it neat and short!
Why It Works (For Novices):
- Fairness: Keeps the first copy of each note, removing later ones—simple rule.
- Tidiness: Shifts notes so there are no gaps, keeping the list ready to use.
- Accuracy: Updates count so you know exactly how many unique notes you have.
Why It’s Designed This Way (For Maintainers):
- Efficiency Goal: Reduces redundant flags for clang (e.g., in collect_code_completion_args),
ensuring clean input on UNIX systems (per _POSIX_C_SOURCE) where duplicates could waste time.
- In-Place Operation: Modifies lines directly, avoiding new allocations, which is memory-efficient
but assumes the caller (e.g., store_lines) is okay with this.
- Memory Safety: Frees duplicates immediately, preventing leaks since lines are strdup’d (e.g.,
from read_files). Assumes caller frees remaining strings later.
- Simple Algorithm: Uses a basic O(n²) comparison with shifting—effective for small lists (typical
for include paths) but not optimized for huge arrays.
- Stability: Preserves order of first occurrences, which might matter for flag precedence in clang.
Maintenance Notes:
- Performance: For large *count (e.g., >100), O(n²) comparisons slow down—consider a hash table
or sorting first (like qsort in store_lines) if this becomes a bottleneck.
- Edge Cases: If *count is 0 or 1, it does nothing (safe); test with duplicates at start/end to
ensure shifting works.
- Memory: Assumes lines[i] are valid pointers—NULLs could crash strcmp. Add a NULL check if
read_files might store them.
- Extensibility: To ignore case or whitespace in duplicates, tweak strcmp—current exact match
is strict but fast.
- Debugging: Log (via log_message) when duplicates are found to trace unexpected repeats in configs.
*/
// Function to remove duplicate lines
void remove_duplicates(char **lines, int *count) {
for(int i = 0; i < *count; i++) {
for(int j = i + 1; j < *count; j++) {
if(strcmp(lines[i], lines[j]) == 0) {
free(lines[j]);
for(int k = j; k < *count - 1; k++) {
lines[k] = lines[k + 1];
}
(*count)--;
j--;
}
}
}
}
/*
Function Description:
Reads include flags from two files (.ccls and compile_flags.txt), removes duplicates, sorts them,
and stores the results in two arrays: one for original order (lines) and one sorted (sorted_lines).
This function prepares a clean, ordered list of compiler flags for later use (e.g., by clang).
Parameters:
- file1 (const char *): Path to the first file (typically .ccls), not modified.
- file2 (const char *): Path to the second file (typically compile_flags.txt), not modified.
- lines (char **): Array of string pointers to store the unique lines in original order.
Caller must ensure it’s at least MAX_LINES and free the strings later.
- sorted_lines (char **): Array to store the same lines, but sorted alphabetically.
Same size and ownership rules as lines.
- count (int *): Pointer to an integer tracking the number of lines; updated with the final count.
Return Value: None
- Modifies lines, sorted_lines, and *count in place.
Detailed Steps:
1. Read and Store Lines:
- Calls read_files to extract "-I" and "-isystem" lines from file1 and file2 into lines.
- Updates *count with the initial number of lines found.
2. Remove Duplicates:
- Calls remove_duplicates on lines, reducing *count to reflect only unique entries.
3. Copy to Sorted Array:
- Loops through lines, copying each pointer to sorted_lines (up to *count).
4. Sort the Lines:
- If *count > 0, uses qsort with compare_strings to sort sorted_lines alphabetically.
Flow and Logic:
- Step 1: Gather all relevant lines from both files into lines.
- Step 2: Clean up by removing duplicates, keeping lines compact.
- Step 3: Duplicate the list into sorted_lines for sorting.
- Step 4: Sort sorted_lines if there’s anything to sort.
- Why this order? Read first to get raw data; deduplicate for efficiency; copy then sort to
preserve original order in lines while providing a sorted version.
How It Works (For Novices):
- Imagine you’re collecting directions from two guidebooks (.ccls and compile_flags.txt) about
where to find tools (like "-I/project/include"), and you want two lists: one as-is (lines) and
one alphabetized (sorted_lines), counting how many (count).
- store_lines is like this organizing task:
- Step 1: Flip through both books with read_files, jotting down directions (e.g., "-I/usr",
"-I/project") in your first notebook (lines), counting them (*count).
- Step 2: Cross out repeats with remove_duplicates (e.g., two "-I/usr" become one), updating
your tally.
- Step 3: Copy the cleaned list into a second notebook (sorted_lines).
- Step 4: If there’s anything in the second notebook, sort it A-to-Z (qsort) so it’s easy to read.
- It’s like making two handy lists from messy notes—one raw, one neat and sorted!
Why It Works (For Novices):
- Completeness: Grabs all the directions you need from both books.
- Cleanliness: No repeats cluttering things up.
- Order: Gives you a sorted version for quick lookup, keeping the original too.
Why It’s Designed This Way (For Maintainers):
- Dual Output: Provides both original (lines) and sorted (sorted_lines) lists, offering flexibility—
original order might matter for clang flag precedence, while sorted aids debugging or display.
- Integration: Builds on read_files and remove_duplicates, reusing their logic for modularity,
key for UNIX config processing (per _POSIX_C_SOURCE).
- Efficiency: Removes duplicates before sorting, reducing qsort’s work (O(n log n) vs. larger n).
Assumes small *count (typical for include paths), so O(n²) in remove_duplicates is fine.
- Memory: lines holds strdup’d strings from read_files; sorted_lines shares pointers, avoiding
extra allocations but tying their lifetimes together—caller must free lines[i].
- Sorting: qsort with compare_strings (strcmp) is standard and fast for small arrays, ensuring
alphabetical order for consistency.
Maintenance Notes:
- Memory Ownership: sorted_lines points to lines’ strings—freeing lines[i] affects both. Document
this to avoid double-free or dangling pointers in callers (e.g., collect_code_completion_args).
- Edge Cases: If *count = 0, qsort skips safely; test with duplicate-heavy inputs to ensure
remove_duplicates scales.
- Extensibility: To filter more flag types (e.g., "-D"), adjust read_files and propagate here.
Add a sort option (e.g., reverse) by tweaking qsort comparator if needed.
- Error Handling: Relies on read_files exiting on failure—consider propagating errors (e.g., return
int) for more control in callers.
- Debugging: Log (via log_message) the final *count or sample lines to verify deduplication and sorting.
*/
// Function to store lines in the array
// Parameters:
// file1: path to the first file, .ccls file
// file2: path to the second file, compile_flags.txt file
// lines: array to store the lines
// sorted_lines: array to store the sorted lines
// count: pointer to the number of lines
void store_lines(const char *file1, const char *file2, char **lines, char **sorted_lines, int *count) {
// Read files and store lines in the array
read_files(file1, file2, lines, count);
remove_duplicates(lines, count);
// Copy the lines to sorted_lines
for(int i = 0; i < *count; i++) {
sorted_lines[i] = lines[i];
}
// Sort the lines if count is valid
if(*count > 0) {
qsort(sorted_lines, (size_t)*count, sizeof(char *), compare_strings);
}
}
/*
Function Description:
Compares two strings for sorting purposes, used as a callback by qsort to order an array of strings.
This function determines which string comes first alphabetically by comparing their characters.
Parameters:
- a (const void *): A pointer to the first string pointer (e.g., a char **), treated as immutable.
- b (const void *): A pointer to the second string pointer (e.g., a char **), treated as immutable.
Return Value:
- int: Returns a negative value if a < b (a comes first), 0 if a == b (equal), or positive if a > b
(b comes first), per qsort’s comparison contract.
Detailed Steps:
1. Dereference Pointers:
- Casts a and b from void* to const char ** to access the string pointers they point to.
- Gets the actual strings by dereferencing once (*(const char **)a and *(const char **)b).
2. Compare Strings:
- Uses strcmp to compare the two strings character-by-character.
- Returns strcmp’s result directly (negative, 0, or positive).
Flow and Logic:
- Step 1: Unpack the pointers qsort gives us to reach the strings.
- Step 2: Let strcmp do the heavy lifting to decide order.
- Why this order? Dereference first to get the data; compare next to decide—simple and direct.
How It Works (For Novices):
- Imagine you’re sorting a pile of notes (like "-I/project", "-I/usr") with qsort, and it needs
help deciding which note goes before another.
- compare_strings is like your sorting rule:
- Step 1: qsort hands you two notes wrapped in boxes (a and b). You open the boxes (cast and
dereference) to see the notes inside (the strings).
- Step 2: Compare the notes with strcmp—like checking letter-by-letter: "-I/p" vs. "-I/u".
If "-I/p" comes first (p < u), say “negative”; if same, say “zero”; if "-I/u" first, say “positive.”
- It’s like telling qsort, “Put this one before that one” based on alphabetical order!
Why It Works (For Novices):
- Simplicity: Uses strcmp, a built-in tool, to compare letters, so you don’t have to write it yourself.
- Accuracy: Follows alphabetical rules (e.g., "a" < "b"), making the sorted list neat.
- Helpfulness: Gives qsort exactly what it needs (negative/zero/positive) to shuffle the notes.
Why It’s Designed This Way (For Maintainers):
- qsort Compatibility: Matches qsort’s required comparator signature (const void *, returns int),
enabling sorting in store_lines for UNIX config flags (per _POSIX_C_SOURCE).
- Efficiency: Relies on strcmp, an optimized standard library function, avoiding custom comparison
logic—fast and reliable for small string arrays like include paths.
- Type Safety: Uses const void * and proper casting to const char **, ensuring no modification of
the strings and safe access, as qsort passes pointers-to-pointers (char ** elements).
- Minimalism: Single-line implementation keeps it focused—compares strings, nothing else.
- Standard Behavior: strcmp’s lexicographical order (ASCII-based) is predictable and matches
typical sorting expectations for flags.
Maintenance Notes:
- Assumptions: Expects a and b to point to valid char **—NULL or invalid pointers crash strcmp.
Ensure store_lines (caller) populates lines safely (e.g., via read_files).
- Extensibility: To change sort order (e.g., reverse), swap a and b in strcmp or negate the result.
For case-insensitive sort, use strcasecmp (with proper includes).
- Edge Cases: Equal strings return 0, preserving their relative order (stable sort with qsort).
Test with duplicates from remove_duplicates to confirm.
- Debugging: If sorting fails (e.g., wrong order), log a and b values (via log_message) to trace
what qsort sees—rare, since strcmp is robust.
- Performance: strcmp is O(n) per comparison, fine for short flags; qsort’s O(n log n) dominates
overall cost in store_lines.
*/
// Function to compare strings
int compare_strings(const void *a, const void *b) {
return strcmp(*(const char **)a, *(const char **)b);
}
int get_clang_target(char *output) {
// Check cache first
if(completion_cache.is_valid && completion_cache.cpu_arch[0] != '\0') {
strncpy(output, completion_cache.cpu_arch, MAX_LINE_LENGTH - 1);
output[MAX_LINE_LENGTH - 1] = '\0';
return 0;
}
FILE *fp = _popen("clang --version", "r");
if(!fp) {
return 1;
}
// Allocate on heap instead of stack
char *buffer = (char *)malloc(MAX_OUTPUT);
if(!buffer) {
_pclose(fp);
return 1;
}
size_t bytes_read = fread(buffer, 1, MAX_OUTPUT - 1, fp);
buffer[bytes_read] = '\0';
_pclose(fp);
char *target_line = strstr(buffer, "Target: ");
if(!target_line) {