Added pfsdoctor. Includes PFS3 v19 large partition and file size support.
[AROS.git] / rom / filesys / console_handler / completion.c
blobac044171984314d3913194848b4584126fef73a6
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];
58 BOOL withinfo;
61 /****************************************************************************************/
63 /* Delay opening of the gadtools.library to the first time InitCompletion is called */
65 static struct completioninfo *InitCompletion(struct filehandle *fh, BOOL withinfo)
67 struct completioninfo *ci = NULL;
68 APTR pool;
70 if (fh->gtbase == NULL)
71 fh->gtbase = OpenLibrary("gadtools.library", 39);
73 if (fh->gfxbase == NULL)
74 fh->gfxbase = (APTR) OpenLibrary("graphics.library", 39);
76 if (fh->lastwritetask && GadToolsBase)
77 if (fh->lastwritetask->tc_Node.ln_Type == NT_PROCESS)
79 if ((pool = CreatePool(MEMF_CLEAR, 1024, 1024)))
81 BOOL ok = FALSE;
83 if ((ci = (struct completioninfo *) AllocPooled(pool, sizeof(*ci))))
85 ci->pool = pool;
86 ci->fh = fh;
87 ci->withinfo = withinfo;
88 NewList(&ci->matchlist);
90 ok = TRUE;
93 if (!ok)
95 DeletePool(pool);
96 ci = NULL;
101 return ci;
104 /****************************************************************************************/
106 static void CleanupCompletion(struct completioninfo *ci)
108 DeletePool(ci->pool);
111 /****************************************************************************************/
113 static void PrepareCompletion(struct filehandle *fh, struct completioninfo *ci)
115 WORD i;
116 BOOL in_quotes = FALSE;
118 /* Find word start */
120 ci->wordstart = i = ci->fh->inputstart;
122 while (i != ci->fh->inputpos)
124 switch (ci->fh->inputbuffer[i++])
126 case '"':
127 in_quotes = !in_quotes;
128 if (in_quotes)
129 ci->wordstart = i;
130 break;
132 case ' ':
133 if (!in_quotes)
134 ci->wordstart = i;
135 break;
139 strncpy(ci->dirpart, &ci->fh->inputbuffer[ci->wordstart], ci->fh->inputpos - ci->wordstart);
140 strcpy(ci->filepart, FilePart(ci->dirpart));
142 *(PathPart(ci->dirpart)) = '\0';
144 ci->wordquoted = in_quotes;
146 D(bug("PrepareCompletion: dirpart = \"%s\" filepart = \"%s\"\n", ci->dirpart, ci->filepart));
149 /****************************************************************************************/
151 static void AddQuotes(struct completioninfo *ci, STRPTR s, LONG s_size)
153 LONG len = strlen(s);
155 if (!ci->wordquoted)
157 if (len < s_size - 1)
158 memmove(s + 1, s, len + 1);
159 s[0] = '"';
161 else
163 len--;
166 if (len < s_size - 3)
168 if ((s[len] != '/') && (s[len] != ':'))
170 s[len + 1] = '"';
171 s[len + 2] = '\0';
176 /****************************************************************************************/
178 static void InsertIntoConBuffer(struct completioninfo *ci, STRPTR s)
180 WORD i = ci->fh->conbufferpos;
182 /* Lame code, but speed is no issue here */
183 while ((ci->fh->conbuffersize < CONSOLEBUFFER_SIZE) && *s)
185 memmove(&ci->fh->consolebuffer[i + 1], &ci->fh->consolebuffer[i], 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, 0 },
211 { TAG_DONE } };
213 tags[2].ti_Data = (IPTR) (ci->withinfo ? "#?" : "~(#?.info)");
215 if ((fr = AllocAslRequest(ASL_FileRequest, tags)))
217 if (AslRequest(fr, NULL))
219 UBYTE c;
221 strcpy(ci->match, fr->fr_Drawer);
222 AddPart(ci->match, fr->fr_File, sizeof(ci->match));
224 if (ci->match[0])
226 if (strchr(ci->match, ' '))
227 AddQuotes(ci, ci->match, sizeof(ci->match));
229 c = ci->match[strlen(ci->match) - 1];
230 if ((c != '/') && (c != ':'))
232 strncat(ci->match, " ", sizeof(ci->match));
235 InsertIntoConBuffer(ci, ci->match);
239 FreeAslRequest(fr);
242 CloseLibrary(AslBase);
244 } /* if ((AslBase = OpenLibrary("asl.library", 36))) */
246 CurrentDir(olddir);
247 UnLock(lock);
251 /****************************************************************************************/
253 static BOOL PreparePattern(struct filehandle *fh, struct completioninfo *ci)
255 WORD parsecode;
257 parsecode = ParsePatternNoCase(ci->filepart, ci->pattern, sizeof(ci->pattern));
258 if (parsecode != -1)
260 if (parsecode == 0)
262 if (ci->withinfo)
264 strncat(ci->filepart, "#?", sizeof(ci->filepart));
266 else
268 strncat(ci->filepart, "~(#?.info)", sizeof(ci->filepart));
270 parsecode = ParsePatternNoCase(ci->filepart, ci->pattern, sizeof(ci->pattern));
274 return (parsecode == 1);
278 /****************************************************************************************/
280 static void AddMatchNode(struct filehandle *fh, struct completioninfo *ci, STRPTR name, WORD type)
282 struct matchnode *mn;
283 struct Node *prev, *check;
284 WORD size;
285 BOOL exists = FALSE;
287 size = strlen(name) + 1 + sizeof(struct matchnode) + ((type > 0) ? 1 : 0);
289 if ((mn = AllocPooled(ci->pool, size)))
291 strcpy(mn->name, name);
292 mn->node.ln_Name = mn->name;
294 if (type == 1)
295 strcat(mn->name, "/");
296 if (type == 2)
297 strcat(mn->name, ":");
299 /* Sort into matchlist */
301 prev = NULL;
302 ForeachNode(&ci->matchlist, check)
304 WORD match;
306 match = Stricmp(mn->name, ((struct matchnode *) check)->name);
307 if (match < 0)
308 break;
309 if (match == 0)
311 exists = TRUE;
312 break;
315 prev = check;
318 if (!exists)
320 Insert(&ci->matchlist, (struct Node *) mn, prev);
321 ci->nummatchnodes++;
323 else
325 FreePooled(ci->pool, mn, size);
330 /****************************************************************************************/
332 static void ScanDir(struct filehandle *fh, struct completioninfo *ci)
334 struct FileInfoBlock *fib;
335 BPTR lock;
337 if ((fib = AllocDosObject(DOS_FIB, 0)))
339 if ((lock = Lock(ci->dirpart, SHARED_LOCK)))
341 if (Examine(lock, fib))
343 while (ExNext(lock, fib))
345 if (MatchPatternNoCase(ci->pattern, fib->fib_FileName))
347 BOOL isdir = (fib->fib_DirEntryType > 0);
349 AddMatchNode(fh, ci, fib->fib_FileName, (isdir ? 1 : 0));
354 UnLock(lock);
357 FreeDosObject(DOS_FIB, fib);
361 /****************************************************************************************/
363 static void ScanVol(struct filehandle *fh, struct completioninfo *ci)
365 struct DosList *dlist;
367 dlist = LockDosList(LDF_READ | LDF_VOLUMES | LDF_DEVICES | LDF_ASSIGNS);
369 while ((dlist = NextDosEntry(dlist, LDF_VOLUMES | LDF_ASSIGNS | LDF_DEVICES)) != NULL)
371 STRPTR devname = AROS_BSTR_ADDR(dlist->dol_Name);
373 if (MatchPatternNoCase(ci->pattern, devname))
375 AddMatchNode(fh, ci, devname, 2);
379 UnLockDosList(LDF_READ | LDF_VOLUMES | LDF_DEVICES | LDF_ASSIGNS);
382 static void DoScan(struct filehandle *fh, struct completioninfo *ci)
384 BPTR lock, olddir;
386 if ((lock = DupLock(((struct Process *) ci->fh->lastwritetask)->pr_CurrentDir)))
388 olddir = CurrentDir(lock);
390 if (ci->dirpart[0] == 0)
391 ScanVol(fh, ci);
392 ScanDir(fh, ci);
394 CurrentDir(olddir);
395 UnLock(lock);
399 /****************************************************************************************/
401 #define BUTTON_EXTRA_WIDTH 16
402 #define BUTTON_EXTRA_HEIGHT 6
403 #define BORDER_X 4
404 #define BORDER_Y 4
405 #define BUTTON_SPACING_X 8
406 #define LV_BUTTON_SPACING_Y 4
407 #define LV_EXTRA_HEIGHT 4
409 #define ID_LISTVIEW 1
410 #define ID_OK 2
411 #define ID_CANCEL 3
413 /****************************************************************************************/
415 static BOOL DoChooseReq(struct filehandle *fh, struct completioninfo *ci)
417 static const char oktext[] = "Ok";
418 static const char canceltext[] = "Cancel";
419 static const char titletext[] = "Select filename";
421 struct RastPort temprp;
422 struct DrawInfo *dri;
423 struct Window *win;
424 struct Gadget *gadlist, *gad, *lvgad;
425 struct NewGadget ng;
426 APTR vi;
427 WORD i, buttonwidth, buttonheight, visible_lv_lines;
428 WORD winwidth, winheight, winleft, wintop;
429 LONG sec, micro, secold, microold;
430 BOOL retval = FALSE;
432 if ((dri = GetScreenDrawInfo(ci->fh->window->WScreen)))
434 if ((vi = GetVisualInfoA(ci->fh->window->WScreen, NULL)))
436 InitRastPort(&temprp);
437 SetFont(&temprp, dri->dri_Font);
439 buttonwidth = TextLength(&temprp, oktext, strlen(oktext));
440 i = TextLength(&temprp, canceltext, strlen(canceltext));
441 buttonwidth = (buttonwidth > i) ? buttonwidth : i;
442 buttonwidth += BUTTON_EXTRA_WIDTH;
444 buttonheight = dri->dri_Font->tf_YSize + BUTTON_EXTRA_HEIGHT;
446 i = ci->nummatchnodes > 15 ? 15 : ci->nummatchnodes;
447 if (i < 4)
448 i = 4;
450 winheight = i * (dri->dri_Font->tf_YSize + 1) + LV_EXTRA_HEIGHT + LV_BUTTON_SPACING_Y + buttonheight
451 + BORDER_Y * 2;
453 winwidth = buttonwidth * 2 + BUTTON_SPACING_X + BORDER_X * 2;
454 i = ci->fh->window->WScreen->Width * 1 / 3;
455 if (i > winwidth)
456 winwidth = i;
458 winleft = ci->fh->window->WScreen->MouseX
459 - (winwidth + ci->fh->window->WScreen->WBorLeft + ci->fh->window->WScreen->WBorRight) / 2;
460 wintop = ci->fh->window->WScreen->MouseY
461 - (winheight + ci->fh->window->WScreen->WBorTop + dri->dri_Font->tf_YSize + 1
462 + ci->fh->window->WScreen->WBorBottom) / 2;
464 gad = CreateContext(&gadlist);
466 ng.ng_LeftEdge = ci->fh->window->WScreen->WBorLeft + BORDER_X;
467 ng.ng_TopEdge = ci->fh->window->WScreen->WBorTop + dri->dri_Font->tf_YSize + 1 + BORDER_Y;
468 ng.ng_Width = winwidth - BORDER_X * 2;
469 ng.ng_Height = winheight - BORDER_Y * 2 - buttonheight - LV_BUTTON_SPACING_Y;
470 ng.ng_GadgetText = NULL;
471 ng.ng_TextAttr = NULL;
472 ng.ng_GadgetID = ID_LISTVIEW;
473 ng.ng_Flags = 0;
474 ng.ng_VisualInfo = vi;
475 ng.ng_UserData = 0;
478 struct TagItem lvtags[] =
480 { GTLV_Labels, (IPTR) &ci->matchlist },
481 { GTLV_ShowSelected, 0 },
482 { GTLV_Selected, 0 },
483 { TAG_DONE } };
485 gad = lvgad = CreateGadgetA(LISTVIEW_KIND, gad, &ng, lvtags);
487 visible_lv_lines = (ng.ng_Height - LV_EXTRA_HEIGHT) / (dri->dri_Font->tf_YSize + 1);
490 ng.ng_TopEdge += ng.ng_Height + LV_BUTTON_SPACING_Y;
491 ng.ng_Width = buttonwidth;
492 ng.ng_Height = buttonheight;
493 ng.ng_GadgetText = oktext;
494 ng.ng_GadgetID = ID_OK;
496 gad = CreateGadgetA(BUTTON_KIND, gad, &ng, NULL);
498 ng.ng_LeftEdge += winwidth - buttonwidth - BORDER_X * 2;
499 ng.ng_GadgetText = canceltext;
500 ng.ng_GadgetID = ID_CANCEL;
502 gad = CreateGadgetA(BUTTON_KIND, gad, &ng, NULL);
504 if (gad)
506 struct TagItem wintags[] =
508 { WA_CustomScreen, (IPTR) ci->fh->window->WScreen },
509 { WA_Title, (IPTR) titletext },
510 { WA_CloseGadget, TRUE },
511 { WA_DragBar, TRUE },
512 { WA_DepthGadget, TRUE },
513 { WA_Activate, TRUE },
514 { WA_AutoAdjust, TRUE },
515 { WA_Left, winleft },
516 { WA_Top, wintop },
517 { WA_InnerWidth, winwidth },
518 { WA_InnerHeight, winheight },
519 { WA_IDCMP, IDCMP_CLOSEWINDOW | IDCMP_RAWKEY | IDCMP_VANILLAKEY | LISTVIEWIDCMP | BUTTONIDCMP },
520 { WA_Gadgets, (IPTR) gadlist },
521 { TAG_DONE }
524 if ((win = OpenWindowTagList(NULL, wintags)))
526 BOOL done = FALSE;
527 BOOL doit = FALSE;
529 CurrentTime(&secold, &microold);
531 while (!done && !doit)
533 struct IntuiMessage *msg;
535 WaitPort(win->UserPort);
537 while ((msg = GT_GetIMsg(win->UserPort)))
539 switch (msg->Class)
541 case IDCMP_CLOSEWINDOW:
542 done = TRUE;
543 break;
545 case IDCMP_VANILLAKEY:
546 switch (msg->Code)
548 case 27:
549 done = TRUE;
550 break;
552 case 13:
553 doit = TRUE;
554 break;
556 break;
558 case IDCMP_RAWKEY:
560 WORD scroll = 0;
561 BOOL page = FALSE;
562 BOOL extreme = FALSE;
564 switch (msg->Code)
566 case RAWKEY_UP:
567 scroll = -1;
568 break;
570 case RAWKEY_DOWN:
571 scroll = 1;
572 break;
574 case RAWKEY_PAGEUP:
575 scroll = -1;
576 page = TRUE;
577 break;
579 case RAWKEY_PAGEDOWN:
580 scroll = 1;
581 page = TRUE;
582 break;
584 case RAWKEY_HOME:
585 scroll = -1;
586 extreme = TRUE;
587 break;
589 case RAWKEY_END:
590 scroll = 1;
591 extreme = TRUE;
592 break;
594 case RAWKEY_NM_WHEEL_UP:
595 scroll = -1;
596 break;
598 case RAWKEY_NM_WHEEL_DOWN:
599 scroll = 1;
600 break;
603 if (msg->Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
605 page = TRUE;
608 if (msg->Qualifier & (IEQUALIFIER_LALT | IEQUALIFIER_RALT | IEQUALIFIER_CONTROL))
610 extreme = TRUE;
613 if (scroll)
615 IPTR getsel;
616 struct TagItem tags[] =
618 { GTLV_Selected, (IPTR) &getsel },
619 { TAG_IGNORE, TRUE },
620 { TAG_DONE } };
621 WORD sel;
623 GT_GetGadgetAttrsA(lvgad, win, NULL, tags);
624 sel = (WORD) getsel;
626 if (extreme)
628 scroll *= ci->nummatchnodes;
630 else if (page)
632 scroll *= visible_lv_lines;
635 sel += scroll;
636 if (sel < 0)
637 sel = ci->nummatchnodes - 1;
638 if (sel >= ci->nummatchnodes)
639 sel = 0;
641 tags[0].ti_Data = (IPTR) sel;
642 tags[1].ti_Tag = GTLV_MakeVisible;
643 tags[1].ti_Data = (IPTR) sel;
645 GT_SetGadgetAttrsA(lvgad, win, NULL, tags);
648 break;
650 case IDCMP_GADGETUP:
651 gad = (struct Gadget *) msg->IAddress;
653 switch (gad->GadgetID)
655 case ID_OK:
656 doit = TRUE;
657 break;
659 case ID_CANCEL:
660 done = TRUE;
661 break;
663 case ID_LISTVIEW:
664 CurrentTime(&sec, &micro);
665 if (DoubleClick(secold, microold, sec, micro))
666 doit = TRUE;
667 secold = sec;
668 microold = micro;
669 break;
671 break;
673 } /* switch(msg->Class) */
675 GT_ReplyIMsg(msg);
677 } /* while((msg = GT_GetIMsg(win->UserPort))) */
679 } /* while (!done && !doit) */
681 if (doit)
683 struct Node *node;
684 IPTR sel;
686 struct TagItem gettags[] =
688 { GTLV_Selected, (IPTR) &sel },
689 { TAG_DONE } };
691 GT_GetGadgetAttrsA(lvgad, win, NULL, gettags);
693 i = 0;
694 ForeachNode(&ci->matchlist, node)
696 if ((WORD) sel == i)
698 AddPart(ci->match, node->ln_Name, sizeof(ci->match));
700 retval = TRUE;
701 break;
703 i++;
706 } /* if (doit) */
708 CloseWindow(win);
710 } /* if ((win = OpenWindowTagList(NULL, wintags))) */
712 } /* if (gad) */
714 FreeGadgets(gadlist);
715 FreeVisualInfo(vi);
717 } /* if ((vi = GetVisualInfoA(ci->fh->window->WScreen, NULL))) */
719 FreeScreenDrawInfo(ci->fh->window->WScreen, dri);
721 } /* if ((dri = GetScreenDrawInfo(fh->window->WScreen))) */
723 return retval;
726 /****************************************************************************************/
728 void Completion(struct filehandle *fh, BOOL withinfo)
730 struct completioninfo *ci;
732 if ((ci = InitCompletion(fh, withinfo)))
734 PrepareCompletion(fh, ci);
736 if (!ci->dirpart[0] && !ci->filepart[0])
738 DoFileReq(fh, ci);
740 else
742 if (PreparePattern(fh, ci))
744 BOOL doprint = FALSE;
746 DoScan(fh, ci);
748 strncpy(ci->match, ci->dirpart, sizeof(ci->match));
750 if (ci->nummatchnodes == 1)
752 AddPart(ci->match, ((struct matchnode *) GetHead(&ci->matchlist))->name, sizeof(ci->match));
754 doprint = TRUE;
756 else if (ci->nummatchnodes > 1)
758 doprint = DoChooseReq(fh, ci);
761 if (doprint)
763 WORD backspaces;
764 UBYTE c;
766 if (strchr(ci->match, ' '))
767 AddQuotes(ci, ci->match, sizeof(ci->match));
769 /* Insert as many backspaces in front of the string,
770 to erase whole "word" first (starting at ci->wordstart)
771 before reprinting expanded filename */
773 backspaces = ci->fh->inputpos - ci->wordstart;
775 memmove(ci->match + backspaces, ci->match, sizeof(ci->match) - backspaces);
776 memset(ci->match, 8, backspaces);
778 c = ci->match[strlen(ci->match) - 1];
779 if ((c != '/') && (c != ':'))
781 strncat(ci->match, " ", sizeof(ci->match));
784 InsertIntoConBuffer(ci, ci->match);
790 CleanupCompletion(ci);
792 } /* if ((ci = InitCompletion())) */
796 /****************************************************************************************/