math.h: Remove non-AROS code, comment out non-implemented functions
[AROS.git] / rom / filesys / console_handler / completion.c
blob9f72a929d7b47d0231619400cb7d71f90905fc1d
1 /*
2 Copyright © 1995-2011, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc:
6 Lang: english
7 */
9 #include <exec/memory.h>
10 #include <dos/dos.h>
11 #include <utility/utility.h>
12 #include <intuition/intuition.h>
13 #include <graphics/rastport.h>
14 #include <libraries/gadtools.h>
15 #include <libraries/asl.h>
16 #include <devices/rawkeycodes.h>
18 #include <proto/exec.h>
19 #include <proto/dos.h>
20 #include <proto/intuition.h>
21 #include <proto/graphics.h>
22 #include <proto/utility.h>
23 #include <proto/alib.h>
24 #include <proto/gadtools.h>
25 #include <proto/asl.h>
26 #include "con_handler_intern.h"
27 #include "support.h"
28 #include "completion.h"
30 #include <string.h>
31 #include <stdlib.h>
33 #define DEBUG 0
34 #include <aros/debug.h>
36 /****************************************************************************************/
38 struct matchnode
40 struct Node node;
41 UBYTE name[1];
44 /****************************************************************************************/
46 struct completioninfo
48 struct filehandle *fh;
49 struct List matchlist;
50 APTR pool;
51 WORD nummatchnodes;
52 WORD wordstart;
53 BOOL wordquoted;
54 UBYTE filepart[256];
55 UBYTE dirpart[256];
56 UBYTE pattern[256*2+3];
57 UBYTE match[256];
60 /****************************************************************************************/
62 /* Delay opening of the gadtools.library to the first time InitCompletion is called */
64 static struct completioninfo *InitCompletion(struct filehandle *fh)
66 struct completioninfo *ci = NULL;
67 APTR pool;
69 if (fh->gtbase == NULL)
70 fh->gtbase = OpenLibrary("gadtools.library", 39);
72 if (fh->gfxbase == NULL)
73 fh->gfxbase = (APTR)OpenLibrary("graphics.library", 39);
75 if (fh->lastwritetask && GadToolsBase)
76 if (fh->lastwritetask->tc_Node.ln_Type == NT_PROCESS)
78 if ((pool = CreatePool(MEMF_CLEAR, 1024, 1024)))
80 BOOL ok = FALSE;
82 if ((ci = (struct completioninfo *)AllocPooled(pool, sizeof(*ci))))
84 ci->pool = pool;
85 ci->fh = fh;
86 NewList(&ci->matchlist);
88 ok = TRUE;
91 if (!ok)
93 DeletePool(pool);
94 ci = NULL;
99 return ci;
102 /****************************************************************************************/
104 static void CleanupCompletion(struct completioninfo *ci)
106 DeletePool(ci->pool);
109 /****************************************************************************************/
111 static void PrepareCompletion(struct filehandle *fh, struct completioninfo *ci)
113 WORD i;
114 BOOL in_quotes = FALSE;
116 /* Find word start */
118 ci->wordstart = i = ci->fh->inputstart;
120 while(i != ci->fh->inputpos)
122 switch(ci->fh->inputbuffer[i++])
124 case '"':
125 in_quotes = !in_quotes;
126 if (in_quotes) ci->wordstart = i;
127 break;
129 case ' ':
130 if (!in_quotes) ci->wordstart = i;
131 break;
135 strncpy(ci->dirpart,
136 &ci->fh->inputbuffer[ci->wordstart],
137 ci->fh->inputpos - ci->wordstart);
138 strcpy(ci->filepart, FilePart(ci->dirpart));
140 *(PathPart(ci->dirpart)) = '\0';
142 ci->wordquoted = in_quotes;
144 D(bug("PrepareCompletion: dirpart = \"%s\" filepart = \"%s\"\n", ci->dirpart, ci->filepart));
147 /****************************************************************************************/
149 static void AddQuotes(struct completioninfo *ci, STRPTR s, LONG s_size)
151 LONG len = strlen(s);
153 if (!ci->wordquoted)
155 if (len < s_size - 1)
156 memmove(s + 1, s, len + 1);
157 s[0] = '"';
159 else
161 len--;
164 if (len < s_size - 3)
166 if ((s[len] != '/') && (s[len] != ':'))
168 s[len + 1] = '"';
169 s[len + 2] = '\0';
174 /****************************************************************************************/
176 static void InsertIntoConBuffer(struct completioninfo *ci, STRPTR s)
178 WORD i = ci->fh->conbufferpos;
180 /* Lame code, but speed is no issue here */
181 while((ci->fh->conbuffersize < CONSOLEBUFFER_SIZE) && *s)
183 memmove(&ci->fh->consolebuffer[i + 1],
184 &ci->fh->consolebuffer[i],
185 ci->fh->conbuffersize - i);
187 ci->fh->consolebuffer[i++] = *s++;
188 ci->fh->conbuffersize++;
192 /****************************************************************************************/
194 static void DoFileReq(struct filehandle *fh, struct completioninfo *ci)
196 struct Library *AslBase;
197 BPTR lock, olddir;
199 if ((lock = DupLock(((struct Process *)ci->fh->lastwritetask)->pr_CurrentDir)))
201 olddir = CurrentDir(lock);
203 if ((AslBase = OpenLibrary("asl.library", 36)))
205 struct FileRequester *fr;
206 struct TagItem tags[] =
208 {ASLFR_Window , (IPTR)ci->fh->window },
209 {ASLFR_DoPatterns , (IPTR)TRUE },
210 {ASLFR_InitialPattern, (IPTR)"~(#?.info)" },
211 {TAG_DONE }
214 if ((fr = AllocAslRequest(ASL_FileRequest, tags)))
216 if (AslRequest(fr, NULL))
218 UBYTE c;
220 strcpy(ci->match, fr->fr_Drawer);
221 AddPart(ci->match, fr->fr_File, sizeof(ci->match));
223 if (ci->match[0])
225 if (strchr(ci->match, ' ')) AddQuotes(ci, ci->match, sizeof(ci->match));
227 c = ci->match[strlen(ci->match) - 1];
228 if ((c != '/') && (c != ':'))
230 strncat(ci->match, " ", sizeof(ci->match));
233 InsertIntoConBuffer(ci, ci->match);
237 FreeAslRequest(fr);
240 CloseLibrary(AslBase);
242 } /* if ((AslBase = OpenLibrary("asl.library", 36))) */
244 CurrentDir(olddir);
245 UnLock(lock);
249 /****************************************************************************************/
251 static BOOL PreparePattern(struct filehandle *fh, struct completioninfo *ci)
253 WORD parsecode;
255 parsecode = ParsePatternNoCase(ci->filepart, ci->pattern, sizeof(ci->pattern));
256 if (parsecode != -1)
258 if (parsecode == 0)
260 strncat(ci->filepart, "~(#?.info)", sizeof(ci->filepart));
261 parsecode = ParsePatternNoCase(ci->filepart, ci->pattern, sizeof(ci->pattern));
265 return (parsecode == 1);
269 /****************************************************************************************/
271 static void AddMatchNode(struct filehandle *fh, struct completioninfo *ci,
272 STRPTR name, WORD type)
274 struct matchnode *mn;
275 struct Node *prev, *check;
276 WORD size;
277 BOOL exists = FALSE;
279 size = strlen(name) + 1 + sizeof(struct matchnode) + ((type > 0) ? 1 : 0);
281 if ((mn = AllocPooled(ci->pool, size)))
283 strcpy(mn->name, name);
284 mn->node.ln_Name = mn->name;
286 if (type == 1) strcat(mn->name, "/");
287 if (type == 2) strcat(mn->name, ":");
289 /* Sort into matchlist */
291 prev = NULL;
292 ForeachNode(&ci->matchlist, check)
294 WORD match;
296 match = Stricmp(mn->name, ((struct matchnode *)check)->name);
297 if (match < 0) break;
298 if (match == 0)
300 exists = TRUE;
301 break;
304 prev = check;
307 if (!exists)
309 Insert(&ci->matchlist, (struct Node *)mn, prev);
310 ci->nummatchnodes++;
312 else
314 FreePooled(ci->pool, mn, size);
319 /****************************************************************************************/
321 static void ScanDir(struct filehandle *fh, struct completioninfo *ci)
323 struct FileInfoBlock *fib;
324 BPTR lock;
326 if ((fib = AllocDosObject(DOS_FIB, 0)))
328 if ((lock = Lock(ci->dirpart, SHARED_LOCK)))
330 if (Examine(lock, fib))
332 while(ExNext(lock, fib))
334 if (MatchPatternNoCase(ci->pattern, fib->fib_FileName))
336 BOOL isdir = (fib->fib_DirEntryType > 0);
338 AddMatchNode(fh, ci, fib->fib_FileName, (isdir ? 1 : 0));
343 UnLock(lock);
346 FreeDosObject(DOS_FIB, fib);
350 /****************************************************************************************/
353 static void ScanVol(struct filehandle *fh, struct completioninfo *ci)
355 struct DosList *dlist;
357 dlist = LockDosList(LDF_READ | LDF_VOLUMES | LDF_DEVICES | LDF_ASSIGNS);
359 while ((dlist = NextDosEntry(dlist, LDF_VOLUMES | LDF_ASSIGNS | LDF_DEVICES)) != NULL)
361 STRPTR devname = AROS_BSTR_ADDR(dlist->dol_Name);
363 if (MatchPatternNoCase(ci->pattern, devname))
365 AddMatchNode(fh, ci, devname, 2);
369 UnLockDosList(LDF_READ | LDF_VOLUMES | LDF_DEVICES | LDF_ASSIGNS);
372 static void DoScan(struct filehandle *fh, struct completioninfo *ci)
374 BPTR lock, olddir;
376 if ((lock = DupLock(((struct Process *)ci->fh->lastwritetask)->pr_CurrentDir)))
378 olddir = CurrentDir(lock);
380 if (ci->dirpart[0] == 0) ScanVol(fh, ci);
381 ScanDir(fh, ci);
383 CurrentDir(olddir);
384 UnLock(lock);
388 /****************************************************************************************/
390 #define BUTTON_EXTRA_WIDTH 16
391 #define BUTTON_EXTRA_HEIGHT 6
392 #define BORDER_X 4
393 #define BORDER_Y 4
394 #define BUTTON_SPACING_X 8
395 #define LV_BUTTON_SPACING_Y 4
396 #define LV_EXTRA_HEIGHT 4
398 #define ID_LISTVIEW 1
399 #define ID_OK 2
400 #define ID_CANCEL 3
402 /****************************************************************************************/
404 static BOOL DoChooseReq(struct filehandle *fh, struct completioninfo *ci)
406 static const char oktext[] = "Ok";
407 static const char canceltext[] = "Cancel";
408 static const char titletext[] = "Select filename";
410 struct RastPort temprp;
411 struct DrawInfo *dri;
412 struct Window *win;
413 struct Gadget *gadlist, *gad, *lvgad;
414 struct NewGadget ng;
415 APTR vi;
416 WORD i, buttonwidth, buttonheight, visible_lv_lines;
417 WORD winwidth, winheight, winleft, wintop;
418 LONG sec, micro, secold, microold;
419 BOOL retval = FALSE;
421 if ((dri = GetScreenDrawInfo(ci->fh->window->WScreen)))
423 if ((vi = GetVisualInfoA(ci->fh->window->WScreen, NULL)))
425 InitRastPort(&temprp);
426 SetFont(&temprp, dri->dri_Font);
428 buttonwidth = TextLength(&temprp, oktext, strlen(oktext));
429 i = TextLength(&temprp, canceltext, strlen(canceltext));
430 buttonwidth = (buttonwidth > i) ? buttonwidth : i;
431 buttonwidth += BUTTON_EXTRA_WIDTH;
433 buttonheight = dri->dri_Font->tf_YSize + BUTTON_EXTRA_HEIGHT;
435 i = ci->nummatchnodes > 15 ? 15 : ci->nummatchnodes;
436 if (i < 4) i = 4;
438 winheight = i * (dri->dri_Font->tf_YSize + 1) +
439 LV_EXTRA_HEIGHT +
440 LV_BUTTON_SPACING_Y +
441 buttonheight +
442 BORDER_Y * 2;
444 winwidth = buttonwidth * 2 + BUTTON_SPACING_X + BORDER_X * 2;
445 i = ci->fh->window->WScreen->Width * 1 / 3;
446 if (i > winwidth) winwidth = i;
448 winleft = ci->fh->window->WScreen->MouseX -
449 (winwidth + ci->fh->window->WScreen->WBorLeft +
450 ci->fh->window->WScreen->WBorRight) / 2;
451 wintop = ci->fh->window->WScreen->MouseY -
452 (winheight + ci->fh->window->WScreen->WBorTop +
453 dri->dri_Font->tf_YSize + 1 +
454 ci->fh->window->WScreen->WBorBottom) / 2;
456 gad = CreateContext(&gadlist);
458 ng.ng_LeftEdge = ci->fh->window->WScreen->WBorLeft + BORDER_X;
459 ng.ng_TopEdge = ci->fh->window->WScreen->WBorTop + dri->dri_Font->tf_YSize + 1 + BORDER_Y;
460 ng.ng_Width = winwidth - BORDER_X * 2;
461 ng.ng_Height = winheight - BORDER_Y * 2 - buttonheight - LV_BUTTON_SPACING_Y;
462 ng.ng_GadgetText = NULL;
463 ng.ng_TextAttr = NULL;
464 ng.ng_GadgetID = ID_LISTVIEW;
465 ng.ng_Flags = 0;
466 ng.ng_VisualInfo = vi;
467 ng.ng_UserData = 0;
470 struct TagItem lvtags[] =
472 {GTLV_Labels , (IPTR)&ci->matchlist },
473 {GTLV_ShowSelected , 0 },
474 {GTLV_Selected , 0 },
475 {TAG_DONE }
478 gad = lvgad = CreateGadgetA(LISTVIEW_KIND, gad, &ng, lvtags);
480 visible_lv_lines = (ng.ng_Height - LV_EXTRA_HEIGHT) / (dri->dri_Font->tf_YSize + 1);
483 ng.ng_TopEdge += ng.ng_Height + LV_BUTTON_SPACING_Y;
484 ng.ng_Width = buttonwidth;
485 ng.ng_Height = buttonheight;
486 ng.ng_GadgetText = oktext;
487 ng.ng_GadgetID = ID_OK;
489 gad = CreateGadgetA(BUTTON_KIND, gad, &ng, NULL);
491 ng.ng_LeftEdge += winwidth - buttonwidth - BORDER_X * 2;
492 ng.ng_GadgetText = canceltext;
493 ng.ng_GadgetID = ID_CANCEL;
495 gad = CreateGadgetA(BUTTON_KIND, gad, &ng, NULL);
497 if (gad)
499 struct TagItem wintags[] =
501 {WA_CustomScreen, (IPTR)ci->fh->window->WScreen },
502 {WA_Title , (IPTR)titletext },
503 {WA_CloseGadget , TRUE },
504 {WA_DragBar , TRUE },
505 {WA_DepthGadget , TRUE },
506 {WA_Activate , TRUE },
507 {WA_AutoAdjust , TRUE },
508 {WA_Left , winleft },
509 {WA_Top , wintop },
510 {WA_InnerWidth , winwidth },
511 {WA_InnerHeight , winheight },
512 {WA_IDCMP , IDCMP_CLOSEWINDOW |
513 IDCMP_RAWKEY |
514 IDCMP_VANILLAKEY |
515 LISTVIEWIDCMP |
516 BUTTONIDCMP },
517 {WA_Gadgets , (IPTR)gadlist },
518 {TAG_DONE }
522 if ((win = OpenWindowTagList(NULL, wintags)))
524 BOOL done = FALSE;
525 BOOL doit = FALSE;
527 CurrentTime(&secold, &microold);
529 while (!done && !doit)
531 struct IntuiMessage *msg;
533 WaitPort(win->UserPort);
535 while((msg = GT_GetIMsg(win->UserPort)))
537 switch(msg->Class)
539 case IDCMP_CLOSEWINDOW:
540 done = TRUE;
541 break;
543 case IDCMP_VANILLAKEY:
544 switch(msg->Code)
546 case 27:
547 done = TRUE;
548 break;
550 case 13:
551 doit = TRUE;
552 break;
554 break;
556 case IDCMP_RAWKEY:
558 WORD scroll = 0;
559 BOOL page = FALSE;
560 BOOL extreme = FALSE;
562 switch(msg->Code)
564 case RAWKEY_UP:
565 scroll = -1;
566 break;
568 case RAWKEY_DOWN:
569 scroll = 1;
570 break;
572 case RAWKEY_PAGEUP:
573 scroll = -1;
574 page = TRUE;
575 break;
577 case RAWKEY_PAGEDOWN:
578 scroll = 1;
579 page = TRUE;
580 break;
582 case RAWKEY_HOME:
583 scroll = -1;
584 extreme = TRUE;
585 break;
587 case RAWKEY_END:
588 scroll = 1;
589 extreme = TRUE;
590 break;
592 case RAWKEY_NM_WHEEL_UP:
593 scroll = -1;
594 break;
596 case RAWKEY_NM_WHEEL_DOWN:
597 scroll = 1;
598 break;
601 if (msg->Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
603 page = TRUE;
606 if (msg->Qualifier & (IEQUALIFIER_LALT | IEQUALIFIER_RALT | IEQUALIFIER_CONTROL))
608 extreme = TRUE;
611 if (scroll)
613 IPTR getsel;
614 struct TagItem tags[] =
616 {GTLV_Selected, (IPTR)&getsel },
617 {TAG_IGNORE , TRUE },
618 {TAG_DONE }
620 WORD sel;
622 GT_GetGadgetAttrsA(lvgad, win, NULL, tags);
623 sel = (WORD)getsel;
625 if (extreme)
627 scroll *= ci->nummatchnodes;
629 else if (page)
631 scroll *= visible_lv_lines;
634 sel += scroll;
635 if (sel < 0) sel = ci->nummatchnodes - 1;
636 if (sel >= ci->nummatchnodes) sel = 0;
638 tags[0].ti_Data = (IPTR)sel;
639 tags[1].ti_Tag = GTLV_MakeVisible;
640 tags[1].ti_Data = (IPTR)sel;
642 GT_SetGadgetAttrsA(lvgad, win, NULL, tags);
645 break;
647 case IDCMP_GADGETUP:
648 gad = (struct Gadget *)msg->IAddress;
650 switch(gad->GadgetID)
652 case ID_OK:
653 doit = TRUE;
654 break;
656 case ID_CANCEL:
657 done = TRUE;
658 break;
660 case ID_LISTVIEW:
661 CurrentTime(&sec, &micro);
662 if (DoubleClick(secold, microold, sec, micro)) doit = TRUE;
663 secold = sec; microold = micro;
664 break;
666 break;
668 } /* switch(msg->Class) */
670 GT_ReplyIMsg(msg);
672 } /* while((msg = GT_GetIMsg(win->UserPort))) */
674 } /* while (!done && !doit) */
676 if (doit)
678 struct Node *node;
679 IPTR sel;
681 struct TagItem gettags[] =
683 {GTLV_Selected, (IPTR)&sel },
684 {TAG_DONE }
687 GT_GetGadgetAttrsA(lvgad, win, NULL, gettags);
689 i = 0;
690 ForeachNode(&ci->matchlist, node)
692 if ((WORD)sel == i)
694 AddPart(ci->match, node->ln_Name, sizeof(ci->match));
696 retval = TRUE;
697 break;
699 i++;
702 } /* if (doit) */
704 CloseWindow(win);
706 } /* if ((win = OpenWindowTagList(NULL, wintags))) */
708 } /* if (gad) */
710 FreeGadgets(gadlist);
711 FreeVisualInfo(vi);
713 } /* if ((vi = GetVisualInfoA(ci->fh->window->WScreen, NULL))) */
715 FreeScreenDrawInfo(ci->fh->window->WScreen, dri);
717 } /* if ((dri = GetScreenDrawInfo(fh->window->WScreen))) */
719 return retval;
722 /****************************************************************************************/
724 void Completion(struct filehandle *fh)
726 struct completioninfo *ci;
728 if ((ci = InitCompletion(fh)))
730 PrepareCompletion(fh, ci);
732 if (!ci->dirpart[0] && !ci->filepart[0])
734 DoFileReq(fh, ci);
736 else
738 if (PreparePattern(fh, ci))
740 BOOL doprint = FALSE;
742 DoScan(fh, ci);
744 strncpy(ci->match, ci->dirpart, sizeof(ci->match));
746 if (ci->nummatchnodes == 1)
748 AddPart(ci->match,
749 ((struct matchnode *)GetHead(&ci->matchlist))->name,
750 sizeof(ci->match));
752 doprint = TRUE;
754 else if (ci->nummatchnodes > 1)
756 doprint = DoChooseReq(fh, ci);
759 if (doprint)
761 WORD backspaces;
762 UBYTE c;
764 if (strchr(ci->match, ' ')) AddQuotes(ci, ci->match, sizeof(ci->match));
766 /* Insert as many backspaces in front of the string,
767 to erase whole "word" first (starting at ci->wordstart)
768 before reprinting expanded filename */
770 backspaces = ci->fh->inputpos - ci->wordstart;
772 memmove(ci->match + backspaces, ci->match, sizeof(ci->match) - backspaces);
773 memset(ci->match, 8, backspaces);
775 c = ci->match[strlen(ci->match) - 1];
776 if ((c != '/') && (c != ':'))
778 strncat(ci->match, " ", sizeof(ci->match));
781 InsertIntoConBuffer(ci, ci->match);
787 CleanupCompletion(ci);
789 } /* if ((ci = InitCompletion())) */
793 /****************************************************************************************/