1 #include "Getline.h"
2
3 std::vector<std::string> inputLines;
4 std::string homeDir;
5
save_session(const std::string & path)6 void save_session(const std::string& path)
7 {
8 std::ofstream ofs(path);
9
10 for(size_t l=0; l<inputLines.size(); l++)
11 ofs << inputLines[l] << std::endl;
12
13 ofs.close();
14 }
15
write_prompt(const std::string & lang,const std::string & pwd,const std::string & promptCh)16 void write_prompt(const std::string& lang, const std::string& pwd, const std::string& promptCh)
17 {
18 //clears current line in console
19 clear_console_line();
20 if(lang != "")
21 {
22 std::cout << c_green << lang << c_white;
23 if(pwd != "")
24 std::cout << ":";
25 }
26 if(pwd != "")
27 std::cout << c_purple << pwd << c_white;
28 if(lang != "" || pwd != "")
29 std::cout << promptCh << " ";
30 }
31
write_input_line(const std::string & lang,const std::string & pwd,const std::string & promptCh,const std::string & str,size_t & sPos,const size_t & usableLength,const size_t & linePos,const size_t & bLinePos)32 void write_input_line(const std::string& lang,
33 const std::string& pwd,
34 const std::string& promptCh,
35 const std::string& str,
36 size_t& sPos,
37 const size_t& usableLength,
38 const size_t& linePos,
39 const size_t& bLinePos)
40 {
41 write_prompt(lang, pwd, promptCh);
42
43 if(sPos + usableLength >= linePos)
44 {
45 if(str.size()-sPos <= usableLength)
46 {
47 std::cout << str.substr(sPos, str.size()-sPos);
48
49 size_t ePos = 0;
50 for(size_t i=ePos; i<bLinePos; i++)
51 std::cout << "\b";
52 }
53 else
54 {
55 std::cout << str.substr(sPos, usableLength);
56
57 size_t ePos = str.size() - sPos - usableLength;
58 for(size_t i=ePos; i<bLinePos; i++)
59 std::cout << "\b";
60 }
61 }
62 else
63 {
64 sPos = str.size() - usableLength;
65 std::cout << str.substr(sPos, usableLength);
66
67 size_t ePos = 0;
68 for(size_t i=ePos; i<bLinePos; i++)
69 std::cout << "\b";
70 }
71
72 //std::cout << std::flush;
73 std::fflush(stdout);
74 }
75
nsm_getch()76 int nsm_getch()
77 {
78 char c;
79
80 #if defined _WIN32 || defined _WIN64
81 c = _getch();
82 #else
83 enable_raw_mode();
84 c = getchar();
85 disable_raw_mode();
86 #endif
87
88 return c;
89 }
90
rnbwcout(const std::string & str)91 int rnbwcout(const std::string& str)
92 {
93 std::stringstream ss;
94 ss << str << std::endl;
95
96 return lolfilter(ss);
97 }
98
rnbwcout(const std::set<std::string> & strs)99 int rnbwcout(const std::set<std::string>& strs)
100 {
101 std::stringstream ss;
102 if(strs.size())
103 {
104 auto str=strs.begin();
105 ss << *str++;
106 for(; str!=strs.end(); ++str)
107 ss << " " << *str;
108 ss << std::endl;
109 }
110
111 return lolfilter(ss);
112 }
113
114 #if defined _WIN32 || defined _WIN64
getline(const std::string & lang,const bool & addPwd,const std::string & promptCh,const int & lolcatActive,std::string & str,bool trackLines,const std::vector<std::string> & tabCompletionStrs)115 int getline(const std::string& lang,
116 const bool& addPwd,
117 const std::string& promptCh,
118 const int& lolcatActive,
119 std::string& str,
120 bool trackLines,
121 const std::vector<std::string>& tabCompletionStrs)
122 {
123 char c;
124 std::string pwd;
125 size_t promptLength = 0, usableLength;
126 int prevConsoleWidth = 100000, currConsoleWidth;
127
128 size_t cLine = inputLines.size();
129 std::string backupStr;
130
131 size_t bLinePos = 0, linePos = str.size(); //line position of insertion
132 size_t sPos = 0;
133
134 if(!addPwd)
135 {
136 promptLength = lang.size() + 2;
137 }
138
139 while(1)
140 {
141 currConsoleWidth = console_width();
142 if(currConsoleWidth != prevConsoleWidth)
143 {
144 prevConsoleWidth = currConsoleWidth;
145 if(addPwd)
146 {
147 pwd = get_pwd();
148 homeDir = home_dir();
149 if(homeDir != "" && homeDir == pwd.substr(0, homeDir.size()))
150 pwd = "~" + pwd.substr(homeDir.size(), pwd.size()-homeDir.size());
151
152 const int MIN_USABLE_LENGTH = currConsoleWidth/2;
153 size_t maxPWDLength = std::max(0, (int)currConsoleWidth - (int)lang.size() - 3 - 1 - 2 - MIN_USABLE_LENGTH);
154 if(pwd.size() > maxPWDLength)
155 pwd = ".." + pwd.substr(pwd.size()-maxPWDLength, maxPWDLength);
156
157 promptLength = lang.size() + pwd.size() + promptCh.size() + 2;
158 }
159 }
160 usableLength = std::max(0, (int)currConsoleWidth - (int)promptLength - 1);
161
162 write_input_line(lang, pwd, promptCh, str, sPos, usableLength, linePos, bLinePos);
163
164 c = _getch();
165
166 if(c == '\r' || c == '\n' || c == 10) //new line
167 {
168 write_prompt(lang, pwd, promptCh);
169 if(str.size() && str[str.size()-1] == '\\')
170 std::cout << str.substr(0, str.size()-1) << std::endl;
171 else
172 std::cout << str << std::endl;
173
174 if(trackLines)
175 if(!inputLines.size() || str != inputLines[inputLines.size()-1])
176 inputLines.push_back(str);
177
178 if(c == 10) // ctrl enter
179 return NSM_SENTER;
180 else
181 return 0;
182 }
183 else if(c == '\t') //tab completion
184 {
185 std::vector<int> searchPosVec, trimPosVec;
186 bool foundCompletions = 0;
187
188 int searchPos = linePos-1,
189 trimPos = -1;
190
191 for(; searchPos >= 0 &&
192 str[searchPos] != '\n'; --searchPos)
193 {
194 if(str[searchPos] == ' ' ||
195 str[searchPos] == '\t' ||
196 str[searchPos] == '"' ||
197 str[searchPos] == '\'' ||
198 str[searchPos] == '`' ||
199 str[searchPos] == '(' ||
200 str[searchPos] == '[' ||
201 str[searchPos] == '<' ||
202 str[searchPos] == '{' ||
203 str[searchPos] == ',')
204 {
205 if(searchPos+1<(int)str.size() && str[searchPos] == ' ' && str[searchPos+1] == ' ')
206 continue;
207
208 searchPosVec.push_back(searchPos + 1);
209 if(trimPos == -1)
210 trimPosVec.push_back(searchPos + 1);
211 else
212 trimPosVec.push_back(trimPos);
213 }
214 if(searchPos && (str[searchPos] == '/' || str[searchPos] == '\\') && trimPos == -1)
215 trimPos = searchPos+1;
216 }
217 if(str[searchPos+1] != ' ')
218 {
219 searchPosVec.push_back(searchPos + 1);
220 if(trimPos == -1)
221 trimPosVec.push_back(searchPos + 1);
222 else
223 trimPosVec.push_back(trimPos);
224 }
225
226 for(int i=searchPosVec.size()-1; i>=0; --i)
227 {
228 searchPos = searchPosVec[i];
229 trimPos = trimPosVec[i];
230
231 Path tabPath;
232 std::set<std::string> paths, programs;
233 std::string searchStr = str.substr(searchPos, linePos-searchPos);
234 strip_leading_whitespace(searchStr);
235 if(searchStr != "")
236 {
237 tabPath.set_file_path_from(searchStr.c_str());
238 makeSearchable(tabPath);
239 paths = lsSetStar(tabPath, -1);
240 }
241
242 if(paths.size())
243 {
244 foundCompletions = 1;
245
246 auto path = paths.begin();
247 std::string foundStr = *path;
248 ++path;
249 for(; path!=paths.end(); ++path)
250 {
251 int pMax = std::min(foundStr.size(), path->size()),
252 pos = 0;
253 for(; pos < pMax; ++pos)
254 {
255 if(foundStr[pos] != (*path)[pos])
256 break;
257 }
258 foundStr = foundStr.substr(0, pos);
259 }
260
261 if(linePos-trimPos < foundStr.size())
262 foundStr = foundStr.substr(linePos-trimPos, foundStr.size()-linePos+trimPos);
263 else
264 foundStr = "";
265
266 if(foundStr == "" && paths.size() != 1)
267 {
268 std::cout << "\n";
269 if(lolcatActive)
270 rnbwcout(paths);
271 else
272 {
273 coutPaths(tabPath.dir, paths, " ", 1, 20);
274 std::cout << std::endl;
275 }
276 }
277 else
278 {
279 if(paths.size() == 1)
280 {
281 if(!foundStr.size() || (foundStr[foundStr.size()-1] != '/' && foundStr[foundStr.size()-1] != '\\'))
282 {
283 //add close quote if tab completion started with a quote and not a directory
284 if(searchPos > 0 && str[searchPos-1] == '"')
285 foundStr += "\"";
286 if(searchPos > 0 && str[searchPos-1] == '\'')
287 foundStr += "'";
288
289 //foundStr += " ";
290 }
291 }
292
293 if(foundStr == "")
294 std::cout << "\a" << std::flush;
295 else
296 {
297 str = str.substr(0, linePos) + foundStr + str.substr(linePos, str.size()-linePos);
298
299 for(size_t i=0; i<foundStr.size(); i++)
300 {
301 ++linePos;
302 if(sPos + usableLength +1 == linePos)
303 ++sPos;
304 }
305 }
306 }
307
308 break;
309 }
310
311 if(searchStr != "")
312 {
313 for(size_t j=0; j<tabCompletionStrs.size(); ++j)
314 {
315 if(searchStr == tabCompletionStrs[j].substr(0, searchStr.size()))
316 programs.insert(tabCompletionStrs[j]);
317 }
318 }
319
320 if(programs.size())
321 {
322 foundCompletions = 1;
323
324 if(programs.size() != 1)
325 {
326 std::cout << "\n";
327 if(lolcatActive)
328 rnbwcout(programs);
329 else
330 {
331 coutPaths("", programs, " ", 0, 20);
332 std::cout << std::endl;
333 }
334 }
335 else
336 {
337 std::string foundStr = *programs.begin();
338 foundStr = foundStr.substr(searchStr.size(), foundStr.size() - searchStr.size());
339 //if(!foundStr.size() || (foundStr[foundStr.size()-1] != '/' && foundStr[foundStr.size()-1] != '\\'))
340 // foundStr += " ";
341
342 if(foundStr == "")
343 std::cout << "\a" << std::flush;
344 else
345 {
346 str = str.substr(0, linePos) + foundStr + str.substr(linePos, str.size()-linePos);
347
348 for(size_t i=0; i<foundStr.size(); i++)
349 {
350 ++linePos;
351 if(sPos + usableLength +1 == linePos)
352 ++sPos;
353 }
354 }
355 }
356 break;
357 }
358 }
359
360 if(!foundCompletions)
361 {
362 for(int i=0; i<2; i++)
363 {
364 str = str.substr(0, linePos) + " " + str.substr(linePos, str.size()-linePos);
365 ++linePos;
366
367 if(sPos + usableLength + 1 == linePos)
368 ++sPos;
369 }
370 }
371 }
372 else if(c == 1) //ctrl a
373 {
374 bLinePos = str.size(); //line position of insertion
375 linePos = sPos = 0;
376 }
377 else if(c == 2) //ctrl b
378 {
379 if(linePos > 0)
380 {
381 --linePos;
382 ++bLinePos;
383
384 if(linePos < sPos)
385 sPos = linePos;
386 }
387 }
388 else if(c == 3 || c == 26) //ctrl c & ctrl z
389 {
390 //clear_console_line();
391 std::cout << std::endl << c_red << "--terminated by user--" << c_white << std::endl;
392
393 return NSM_KILL;
394 }
395 else if(c == 4) //ctrl d
396 {
397 str = str.substr(0, linePos);
398 bLinePos = 0;
399 }
400 else if(c == 5) //ctrl e
401 {
402 bLinePos = sPos = 0;
403 linePos = str.size();
404 }
405 else if(c == 6) //ctrl f
406 {
407 if(bLinePos > 0)
408 {
409 ++linePos;
410 --bLinePos;
411
412 if(sPos + usableLength == linePos)
413 ++sPos;
414 }
415 }
416 else if(c == 8) //backspace
417 {
418 if(linePos > 0)
419 {
420 str = str.substr(0, linePos-1) + str.substr(linePos, str.size()-linePos);
421 --linePos;
422
423 if(sPos > 0 && str.size() - sPos < usableLength)
424 --sPos;
425 }
426 }
427 else if(c == 27 || c == 127) //ctrl [ or ctrl backspace
428 {
429 bool foundNonWhitespace = 0;
430 do
431 {
432 if(linePos > 0)
433 {
434 if(str[linePos-1] != ' ' && str[linePos-1] != '\t')
435 foundNonWhitespace = 1;
436
437 str = str.substr(0, linePos-1) + str.substr(linePos, str.size()-linePos);
438 --linePos;
439
440 if(sPos > 0 && str.size() - sPos < usableLength)
441 --sPos;
442 }
443 else
444 break;
445 }while(!foundNonWhitespace || (linePos > 0 && std::isalnum(str[linePos-1])));
446 }
447 else if(c == 29) //ctrl ]
448 {
449 bool foundNonWhitespace = 0;
450 char c = 'a';
451
452 while(!foundNonWhitespace || std::isalnum(c))
453 {
454 if(bLinePos > 0)
455 {
456 c = str[linePos];
457
458 if(c != ' ' && c != '\t')
459 foundNonWhitespace = 1;
460
461 str = str.substr(0, linePos) + str.substr(linePos + 1, str.size()-linePos+1);
462 --bLinePos;
463
464 if(sPos + usableLength == linePos)
465 ++sPos;
466 }
467 else
468 break;
469 }
470 }
471 else if(c == 18) //ctrl+r (same as opt/alt+enter)
472 {
473 write_prompt(lang, pwd, promptCh);
474 std::cout << str << std::endl;
475
476 if(trackLines)
477 if(!inputLines.size() || str != inputLines[inputLines.size()-1])
478 inputLines.push_back(str);
479
480 return NSM_SENTER;
481 }
482 else if(c == 0 || c == -32) //check for arrow keys
483 {
484 c = _getch();
485
486 if(c == -108) //ctrl+tab
487 {
488 //std::cout << "\a" << std::flush;
489
490 for(int i=0; i<2; i++)
491 {
492 str = str.substr(0, linePos) + " " + str.substr(linePos, str.size()-linePos);
493 ++linePos;
494
495 if(sPos + usableLength + 1 == linePos)
496 ++sPos;
497 }
498 }
499
500 if(c == -115) //ctrl up arrow
501 {
502 }
503 else if(c == -111) //ctrl down arrow
504 {
505 }
506 else if(c == 116) //ctrl right arrow
507 {
508 do
509 {
510 if(bLinePos > 0)
511 {
512 ++linePos;
513 --bLinePos;
514
515 if(sPos + usableLength == linePos)
516 ++sPos;
517 }
518 }while(bLinePos > 0 && std::isalnum(str[linePos]));
519 }
520 else if(c == 115) //ctrl left arrow
521 {
522 do
523 {
524 if(linePos > 0)
525 {
526 --linePos;
527 ++bLinePos;
528
529 if(linePos < sPos)
530 sPos = linePos;
531 }
532 }while(linePos > 0 && std::isalnum(str[linePos]));
533 }
534
535 else if(c == 72) //up arrow
536 {
537 if(cLine > 0)
538 {
539 if(cLine == inputLines.size())
540 backupStr = str;
541 --cLine;
542 str = inputLines[cLine];
543 bLinePos = sPos = 0;
544 linePos = str.size();
545 }
546 }
547 else if(c == 80) //down arrow
548 {
549 if(cLine < inputLines.size())
550 {
551 ++cLine;
552 if(cLine == inputLines.size())
553 str = backupStr;
554 else
555 str = inputLines[cLine];
556 bLinePos = sPos = 0;
557 linePos = str.size();
558 }
559 }
560 else if(c == 77) //right arrow
561 {
562 if(bLinePos > 0)
563 {
564 ++linePos;
565 --bLinePos;
566
567 if(sPos + usableLength == linePos)
568 ++sPos;
569 }
570 }
571 else if(c == 75) //left arrow
572 {
573 if(linePos > 0)
574 {
575 --linePos;
576 ++bLinePos;
577
578 if(linePos < sPos)
579 sPos = linePos;
580 }
581 }
582 }
583 else
584 {
585 str = str.substr(0, linePos) + c + str.substr(linePos, str.size()-linePos);
586 ++linePos;
587
588 if(sPos + usableLength +1 == linePos)
589 ++sPos;
590 }
591
592 write_input_line(lang, pwd, promptCh, str, sPos, usableLength, linePos, bLinePos);
593 }
594
595 return 0;
596 }
597 #else //*nix
getline(const std::string & lang,const bool & addPwd,const std::string & promptCh,const int & lolcatActive,std::string & str,bool trackLines,const std::vector<std::string> & tabCompletionStrs)598 int getline(const std::string& lang,
599 const bool& addPwd,
600 const std::string& promptCh,
601 const int& lolcatActive,
602 std::string& str,
603 bool trackLines,
604 const std::vector<std::string>& tabCompletionStrs)
605 {
606 char c;
607 std::string pwd;
608 size_t promptLength = 0, usableLength;
609 int prevConsoleWidth = 100000, currConsoleWidth;
610
611 size_t cLine = inputLines.size();
612 std::string backupStr;
613
614 size_t bLinePos = 0, linePos = str.size(); //line position of insertion
615 size_t sPos = 0;
616
617 if(!addPwd)
618 {
619 promptLength = lang.size() + 2;
620 }
621
622 enable_raw_mode();
623 while(1)
624 {
625 currConsoleWidth = console_width();
626 if(currConsoleWidth != prevConsoleWidth)
627 {
628 prevConsoleWidth = currConsoleWidth;
629 if(addPwd)
630 {
631 pwd = get_pwd();
632 homeDir = home_dir();
633 if(homeDir != "" && homeDir == pwd.substr(0, homeDir.size()))
634 pwd = "~" + pwd.substr(homeDir.size(), pwd.size()-homeDir.size());
635 #if defined __FreeBSD__
636 else
637 {
638 homeDir = "/usr" + homeDir;
639 if(homeDir != "" && homeDir == pwd.substr(0, homeDir.size()))
640 pwd = "~" + pwd.substr(homeDir.size(), pwd.size()-homeDir.size());
641 }
642 #endif
643
644 const int MIN_USABLE_LENGTH = currConsoleWidth/2;
645 size_t maxPWDLength = std::max(0, (int)currConsoleWidth - (int)lang.size() - 3 - 1 - 2 - MIN_USABLE_LENGTH);
646 if(pwd.size() > maxPWDLength)
647 pwd = ".." + pwd.substr(pwd.size()-maxPWDLength, maxPWDLength);
648
649 promptLength = lang.size() + pwd.size() + promptCh.size() + 2;
650 }
651 }
652 usableLength = std::max(0, (int)currConsoleWidth - (int)promptLength - 1);
653
654 write_input_line(lang, pwd, promptCh, str, sPos, usableLength, linePos, bLinePos);
655
656 c = getchar();
657
658 if(c == '\r' || c == '\n') //new line
659 {
660 write_prompt(lang, pwd, promptCh);
661 if(str.size() && str[str.size()-1] == '\\')
662 std::cout << str.substr(0, str.size()-1) << std::endl;
663 else
664 std::cout << str << std::endl;
665
666 if(trackLines)
667 if(!inputLines.size() || str != inputLines[inputLines.size()-1])
668 inputLines.push_back(str);
669 disable_raw_mode(); //system("stty cooked");
670 return 0;
671 }
672 else if(c == '\t') //tab completion
673 {
674 std::vector<int> searchPosVec, trimPosVec;
675 bool foundCompletions = 0;
676
677 int searchPos = linePos-1,
678 trimPos = -1;
679
680 for(; searchPos >= 0 &&
681 str[searchPos] != '\n'; --searchPos)
682 {
683 if(str[searchPos] == ' ' ||
684 str[searchPos] == '\t' ||
685 str[searchPos] == '"' ||
686 str[searchPos] == '\'' ||
687 str[searchPos] == '`' ||
688 str[searchPos] == '(' ||
689 str[searchPos] == '[' ||
690 str[searchPos] == '<' ||
691 str[searchPos] == '{' ||
692 str[searchPos] == ',')
693 {
694 if(searchPos+1<(int)str.size() && str[searchPos] == ' ' && str[searchPos+1] == ' ')
695 continue;
696
697 searchPosVec.push_back(searchPos + 1);
698 if(trimPos == -1)
699 trimPosVec.push_back(searchPos + 1);
700 else
701 trimPosVec.push_back(trimPos);
702 }
703 if(searchPos && (str[searchPos] == '/' || str[searchPos] == '\\') && trimPos == -1)
704 trimPos = searchPos+1;
705 }
706 if(str[searchPos+1] != ' ')
707 {
708 searchPosVec.push_back(searchPos + 1);
709 if(trimPos == -1)
710 trimPosVec.push_back(searchPos + 1);
711 else
712 trimPosVec.push_back(trimPos);
713 }
714
715 for(int i=searchPosVec.size()-1; i>=0; --i)
716 {
717 searchPos = searchPosVec[i];
718 trimPos = trimPosVec[i];
719
720 Path tabPath;
721 std::set<std::string> paths, programs;
722 std::string searchStr = str.substr(searchPos, linePos-searchPos);
723 strip_leading_whitespace(searchStr);
724 if(searchStr != "")
725 {
726 tabPath.set_file_path_from(searchStr.c_str());
727 makeSearchable(tabPath);
728 paths = lsSetStar(tabPath, -1);
729 }
730
731 if(paths.size())
732 {
733 foundCompletions = 1;
734
735 auto path = paths.begin();
736 std::string foundStr = *path;
737 ++path;
738 for(; path!=paths.end(); ++path)
739 {
740 int pMax = std::min(foundStr.size(), path->size()),
741 pos = 0;
742 for(; pos < pMax; ++pos)
743 {
744 if(foundStr[pos] != (*path)[pos])
745 break;
746 }
747 foundStr = foundStr.substr(0, pos);
748 }
749
750 if(linePos-trimPos < foundStr.size())
751 foundStr = foundStr.substr(linePos-trimPos, foundStr.size()-linePos+trimPos);
752 else
753 foundStr = "";
754
755 if(foundStr == "" && paths.size() != 1)
756 {
757 std::cout << "\n";
758 if(lolcatActive)
759 rnbwcout(paths);
760 else
761 {
762 coutPaths(tabPath.dir, paths, " ", 1, 20);
763 std::cout << std::endl;
764 }
765 }
766 else
767 {
768 if(paths.size() == 1)
769 {
770 if(!foundStr.size() || (foundStr[foundStr.size()-1] != '/' && foundStr[foundStr.size()-1] != '\\'))
771 {
772 //add close quote if tab completion started with a quote and not a directory
773 if(searchPos > 0 && str[searchPos-1] == '"')
774 foundStr += "\"";
775 if(searchPos > 0 && str[searchPos-1] == '\'')
776 foundStr += "'";
777
778 //foundStr += " ";
779 }
780 }
781
782 if(foundStr == "")
783 std::cout << "\a" << std::flush;
784 else
785 {
786 str = str.substr(0, linePos) + foundStr + str.substr(linePos, str.size()-linePos);
787
788 for(size_t i=0; i<foundStr.size(); i++)
789 {
790 ++linePos;
791 if(sPos + usableLength +1 == linePos)
792 ++sPos;
793 }
794 }
795 }
796
797 break;
798 }
799
800 if(searchStr != "")
801 {
802 for(size_t j=0; j<tabCompletionStrs.size(); ++j)
803 {
804 if(searchStr == tabCompletionStrs[j].substr(0, searchStr.size()))
805 programs.insert(tabCompletionStrs[j]);
806 }
807 }
808
809 if(programs.size())
810 {
811 foundCompletions = 1;
812
813 if(programs.size() != 1)
814 {
815 std::cout << "\n";
816 if(lolcatActive)
817 rnbwcout(programs);
818 else
819 {
820 coutPaths("", programs, " ", 0, 20);
821 std::cout << std::endl;
822 }
823 }
824 else
825 {
826 std::string foundStr = *programs.begin();
827 foundStr = foundStr.substr(searchStr.size(), foundStr.size() - searchStr.size());
828 //if(!foundStr.size() || (foundStr[foundStr.size()-1] != '/' && foundStr[foundStr.size()-1] != '\\'))
829 // foundStr += " ";
830
831 if(foundStr == "")
832 std::cout << "\a" << std::flush;
833 else
834 {
835 str = str.substr(0, linePos) + foundStr + str.substr(linePos, str.size()-linePos);
836
837 for(size_t i=0; i<foundStr.size(); i++)
838 {
839 ++linePos;
840 if(sPos + usableLength +1 == linePos)
841 ++sPos;
842 }
843 }
844 }
845 break;
846 }
847 }
848
849 if(!foundCompletions)
850 {
851 for(int i=0; i<2; i++)
852 {
853 str = str.substr(0, linePos) + " " + str.substr(linePos, str.size()-linePos);
854 ++linePos;
855
856 if(sPos + usableLength + 1 == linePos)
857 ++sPos;
858 }
859 }
860 }
861 else if(c == 1) //ctrl a
862 {
863 bLinePos = str.size(); //line position of insertion
864 linePos = sPos = 0;
865 }
866 else if(c == 2) //ctrl b
867 {
868 if(linePos > 0)
869 {
870 --linePos;
871 ++bLinePos;
872
873 if(linePos < sPos)
874 sPos = linePos;
875 }
876 }
877 else if(c == 4) //ctrl d
878 {
879 str = str.substr(0, linePos);
880 bLinePos = 0;
881 }
882 else if(c == 5) //ctrl e
883 {
884 bLinePos = sPos = 0;
885 linePos = str.size();
886 }
887 else if(c == 6) //ctrl f
888 {
889 if(bLinePos > 0)
890 {
891 ++linePos;
892 --bLinePos;
893
894 if(sPos + usableLength == linePos)
895 ++sPos;
896 }
897 }
898 #if defined __FreeBSD__
899 else if(c == 127 || c == 31) //ctrl backspace or cmd [ or ctrl (shift) -
900 #else //unix
901 else if(c == 8 || c == 31) //ctrl backspace or cmd [ or ctrl (shift) -
902 #endif
903 {
904 bool foundNonWhitespace = 0;
905 do
906 {
907 if(linePos > 0)
908 {
909 if(str[linePos-1] != ' ' && str[linePos-1] != '\t')
910 foundNonWhitespace = 1;
911
912 str = str.substr(0, linePos-1) + str.substr(linePos, str.size()-linePos);
913 --linePos;
914
915 if(sPos > 0 && str.size() - sPos < usableLength)
916 --sPos;
917 }
918 else
919 break;
920 }while(!foundNonWhitespace || (linePos > 0 && std::isalnum(str[linePos-1])));
921 }
922 else if(c == 29) //ctrl ]
923 {
924 bool foundNonWhitespace = 0;
925 char c = 'a';
926
927 while(!foundNonWhitespace || std::isalnum(c))
928 {
929 if(bLinePos > 0)
930 {
931 c = str[linePos];
932
933 if(c != ' ' && c != '\t')
934 foundNonWhitespace = 1;
935
936 str = str.substr(0, linePos) + str.substr(linePos + 1, str.size()-linePos+1);
937 --bLinePos;
938
939 if(sPos + usableLength == linePos)
940 ++sPos;
941 }
942 else
943 break;
944 }
945 }
946 #if defined __FreeBSD__
947 else if(c == 8) //backspace
948 #else //unix
949 else if(c == 127) //backspace
950 #endif
951 {
952 if(linePos > 0)
953 {
954 str = str.substr(0, linePos-1) + str.substr(linePos, str.size()-linePos);
955 --linePos;
956
957 if(sPos > 0 && str.size() - sPos < usableLength)
958 --sPos;
959 }
960 }
961 else if(c == 18) //ctrl+r (same as opt/alt+enter)
962 {
963 write_prompt(lang, pwd, promptCh);
964 std::cout << str << std::endl;
965
966 if(trackLines)
967 if(!inputLines.size() || str != inputLines[inputLines.size()-1])
968 inputLines.push_back(str);
969 disable_raw_mode(); //system("stty cooked");
970
971 return NSM_SENTER;
972 }
973 else if(c == '\33' || c == 27) //check for arrow keys
974 {
975 c = getchar();
976
977 if(c == 10) //option/alt + enter
978 {
979 write_prompt(lang, pwd, promptCh);
980 std::cout << str << std::endl;
981
982 if(trackLines)
983 if(!inputLines.size() || str != inputLines[inputLines.size()-1])
984 inputLines.push_back(str);
985 disable_raw_mode(); //system("stty cooked");
986
987 return NSM_SENTER;
988 }
989 else if(c == 91)
990 {
991 c = getchar();
992
993 if(c == 49)
994 {
995 c = getchar();
996
997 if(c == 59)
998 {
999 c = getchar();
1000
1001 if(c == 51 || c == 53) //alt or ctrl
1002 {
1003 c = getchar();
1004
1005 if(c == 65) //alt+up and ctrl+up
1006 {
1007 }
1008 else if(c == 66) //alt+down and ctrl+down
1009 {
1010 }
1011 else if(c == 67) //alt+right and ctrl+right
1012 {
1013 do
1014 {
1015 if(bLinePos > 0)
1016 {
1017 ++linePos;
1018 --bLinePos;
1019
1020 if(sPos + usableLength == linePos)
1021 ++sPos;
1022 }
1023 }while(bLinePos > 0 && std::isalnum(str[linePos]));
1024 }
1025 else if(c == 68) //alt+left and ctrl+left
1026 {
1027 do
1028 {
1029 if(linePos > 0)
1030 {
1031 --linePos;
1032 ++bLinePos;
1033
1034 if(linePos < sPos)
1035 sPos = linePos;
1036 }
1037 }while(linePos > 0 && std::isalnum(str[linePos]));
1038 }
1039 }
1040 }
1041 }
1042 else if(c == 65) //up arrow
1043 {
1044 if(cLine > 0)
1045 {
1046 if(cLine == inputLines.size())
1047 backupStr = str;
1048 --cLine;
1049 str = inputLines[cLine];
1050 bLinePos = sPos = 0;
1051 linePos = str.size();
1052 }
1053 }
1054 else if(c == 66) //down arrow
1055 {
1056 if(cLine < inputLines.size())
1057 {
1058 ++cLine;
1059 if(cLine == inputLines.size())
1060 str = backupStr;
1061 else
1062 str = inputLines[cLine];
1063 bLinePos = sPos = 0;
1064 linePos = str.size();
1065 }
1066 }
1067 else if(c == 67) //right arrow
1068 {
1069 if(bLinePos > 0)
1070 {
1071 ++linePos;
1072 --bLinePos;
1073
1074 if(sPos + usableLength == linePos)
1075 ++sPos;
1076 }
1077 }
1078 else if(c == 68) //left arrow
1079 {
1080 if(linePos > 0)
1081 {
1082 --linePos;
1083 ++bLinePos;
1084
1085 if(linePos < sPos)
1086 sPos = linePos;
1087 }
1088 }
1089 else if(c == 90) //shift tab
1090 std::cout << "\a" << std::flush;
1091 }
1092 #if defined __APPLE__
1093 else if(c == 91)
1094 {
1095 c = getchar();
1096
1097 if(c == 65) //opt+up
1098 {}
1099 else if(c == 66) //opt+down
1100 {}
1101 }
1102 else if(c == 98) //opt+left
1103 {
1104 do
1105 {
1106 if(linePos > 0)
1107 {
1108 --linePos;
1109 ++bLinePos;
1110
1111 if(linePos < sPos)
1112 sPos = linePos;
1113 }
1114 }while(linePos > 0 && std::isalnum(str[linePos]));
1115 }
1116 else if(c == 102) //opt+right
1117 {
1118 do
1119 {
1120 if(bLinePos > 0)
1121 {
1122 ++linePos;
1123 --bLinePos;
1124
1125 if(sPos + usableLength == linePos)
1126 ++sPos;
1127 }
1128 }while(bLinePos > 0 && std::isalnum(str[linePos]));
1129 }
1130 #endif
1131 else if(c == 98) //alt b
1132 {
1133 do
1134 {
1135 if(linePos > 0)
1136 {
1137 --linePos;
1138 ++bLinePos;
1139
1140 if(linePos < sPos)
1141 sPos = linePos;
1142 }
1143 }while(linePos > 0 && std::isalnum(str[linePos]));
1144 }
1145 else if(c == 102) //alt f
1146 {
1147 do
1148 {
1149 if(bLinePos > 0)
1150 {
1151 ++linePos;
1152 --bLinePos;
1153
1154 if(sPos + usableLength == linePos)
1155 ++sPos;
1156 }
1157 }while(bLinePos > 0 && std::isalnum(str[linePos]));
1158 }
1159 else if(c == 127) //alt backspace
1160 {
1161 bool foundNonWhitespace = 0;
1162 do
1163 {
1164 if(linePos > 0)
1165 {
1166 if(str[linePos-1] != ' ' && str[linePos-1] != '\t')
1167 foundNonWhitespace = 1;
1168
1169 str = str.substr(0, linePos-1) + str.substr(linePos, str.size()-linePos);
1170 --linePos;
1171
1172 if(sPos > 0 && str.size() - sPos < usableLength)
1173 --sPos;
1174 }
1175 else
1176 break;
1177 }while(!foundNonWhitespace || (linePos > 0 && std::isalnum(str[linePos-1])));
1178 }
1179 }
1180 else
1181 {
1182 str = str.substr(0, linePos) + c + str.substr(linePos, str.size()-linePos);
1183 ++linePos;
1184
1185 if(sPos + usableLength +1 == linePos)
1186 ++sPos;
1187 }
1188
1189 //write_input_line(lang, pwd, promptCh, str, sPos, usableLength, linePos, bLinePos);
1190 }
1191
1192 disable_raw_mode();
1193
1194 return 0;
1195 }
1196 #endif
1197