/***************************************************************************** Application ASH: findfile.c (c) Pierre Adriaans 1994 ------------------------------------------------------------------------------ Module de gestion de la recherche de fichiers *****************************************************************************/ #include "ash.h" /* Descripteur de fichier */ struct EleFind_s { char *Path; /* Argument de chdir() */ char *FileName; /* Nom du fichier trouve */ char DispLine[80]; /* Ligne d'affichage dans la fenetre */ }; /* Element de la liste */ struct Find_s { struct EleFind_s Ele; struct Find_s *Suivant; struct Find_s *Prec; }; typedef struct Find_s Find_t; static Find_t *Find = (Find_t *)NULL, /* Pointeur de tete de la liste */ *EndFind; /* Pointeur sur le dernier element de la liste */ static int FindCount, /* Nombre de fichiers trouves */ SubDirCount; /* Compteur de ss-rep */ extern char *Home; extern DIR *DummyDir; extern AnswerBoxItem_t *InfoMsg; extern char DummyStr[DUMMYSTR_SIZE]; extern DirList_t *Current[NB_MAX_WINS]; extern int AL; int FindFile(void); int SearchToFind(char *SearchMask,char *SearchStart); /***************************************************************************** FindFile() ------------------------------------------------------------------------------ Input: / Process: fonction de recherche de fichiers avec affichage et changement de ss-rep si necessaire. Output: 1 si sortie par Enter et donc cd 0 si sortie par Esc et donc rien a faire *****************************************************************************/ int FindFile(void) { char Line[80]; /* Ligne transversale */ int Lig,Col,Height,Length, /* Coordonnees exterieures de la fenetre */ ActLig,ActCol, ActHeight,ActLength, /* Cooredonnees de la fenetre d'affichage */ Fini = 0, /* Temoin de fin de boucle */ i,j,Cpt, /* Compteurs */ SelIndex, /* Index de selection */ X,Y, /* Coord du curseur */ rc, /* Code de retour */ NbreEntries, /* Nombre de ss-rep */ InvLig,InvCol; /* Coord de la ligne inverse actuelle */ Find_t *CurrentFind, /* ss-rep actuellement en inverse */ *Dummy,*Temp; /* Pointeurs de manipulation */ char StrTemp[255], /* String tampon */ SearchMask[TAGMASK_LENGTH + 1], /* Masque de recherche */ SearchStart[FILENAME_LENGTH], /* ss-rep de depart */ SaveFile[PATH_LENGTH + 1]; /* Fichier ou sauver */ FILE *hf; /* Pour sauver */ /* Recuperer la position du curseur */ GetXY(&Y,&X); /* Saisir les caracteristiques de la recherche */ strcpy(SearchMask,"*.*"); if(InputBox(SearchMask,TAGMASK_LENGTH,FileOpNorm,FileOpInv," Find File ", "Enter search mask:",0,0,DOUBLE_FRAMED) != 0) return(0); strcpy(SearchStart,Home); if(InputBox(SearchStart,PATH_LENGTH,FileOpNorm,FileOpInv," Find File ", "Where should I search from?",0,0,DOUBLE_FRAMED) != 0) return(0); /* Ouvrir la fenetre de patience */ OpenWin(10,20,5,40,DoingNorm,SINGLE_FRAMED,SHADED); WriteString("Searching directory tree...",DoingNorm,11,26); WriteString(" Press any key to cancel.",DoingNorm,13,26); /* Mise a zero des compteurs et a NULL du pointeur de tete */ FindCount = 0; SubDirCount = 0; Find = (Find_t *)NULL; /* Appel a la fonction recursive */ if(SearchToFind(SearchMask,SearchStart) == -1) { CloseWin(); return(0); } CloseWin(); if(FindCount == 0) { /* Rien trouve */ InitAnswerBoxItem(&InfoMsg); AddAnswerBoxItem(&InfoMsg," No match found. "); InfoBox(ErrorNorm,ErrorInv," Find File ",InfoMsg,0,0,DOUBLE_FRAMED); return(0); } /* Fixer les coordonnes de la fenetre */ Lig = 3; Col = 3; Height = 20; Length = 76; /* Construire la fenetre */ Line[0] = F_BDSD; for(i=1;i<(Length-7);i++) Line[i] = F_S_HO; Line[i] = F_SDBD; Line[i+1] = 0; OpenWin(Lig,Col,Height,Length,FindFileNorm,UNFRAMED,SHADED); Lig +=1; Col += 3; Height -= 2; Length -= 6; ActLig = Lig + 1; ActCol = Col + 2; ActHeight = Height - 5; ActLength = Length - 4; PaintFrame(Lig,Col,Height,Length,FindFileNorm,DOUBLE_FRAMED); WriteString(" Search Results ",FindFileNorm,Lig,33); WriteString(Line,FindFileNorm,Lig+Height-4,Col); WriteString("Up/Down arrows to move, Enter to reach a file,",FindFileNorm, Lig+Height-3,17); WriteString("Ctrl-W to save the list, Ctrl-C (Esc) to cancel.", FindFileNorm,Lig+Height-2,16); /* Mettre le curseur hors du chemin */ GotoXY(Y,X); /* Placer EndFind sur le dernier element de la liste */ for(EndFind = Find; EndFind->Suivant != (Find_t *)NULL; EndFind = EndFind->Suivant); /* Afficher du debut */ for(Cpt = ActLig,Dummy = Find; Cpt < (ActLig + ActHeight) && Dummy != (Find_t *)NULL; Cpt++,Dummy = Dummy->Suivant) WriteString(Dummy->Ele.DispLine,FindFileNorm,Cpt,ActCol); /* Fixer les coordonnees de la zone d'inversion */ InvLig = ActLig; InvCol = ActCol; /* Inverser l'option courante */ PaintString(InvLig,InvCol,ActLength,FindFileInv); /* Affecter les variables de controle */ CurrentFind = Find; SelIndex = 1; NbreEntries = FindCount; /* And on y va ! */ while(!Fini) { switch(ReadKbd()) { case CTRL_W: /* Sauver la liste */ strcpy(SaveFile,"findlist.ash"); if(InputBox(SaveFile,PATH_LENGTH - 1,FindFileSaveNorm,FindFileSaveInv, " Find File ", "Enter a filename to save the current search results:",1,0, DOUBLE_FRAMED) != 0) break; if((hf = fopen(SaveFile,"w")) == (FILE *)NULL) { InitAnswerBoxItem(&InfoMsg); AddAnswerBoxItem(&InfoMsg, "ERROR: impossible to create the output file"); AddAnswerBoxItem(&InfoMsg,SaveFile); switch(errno) { case EACCES: AddAnswerBoxItem(&InfoMsg,"Permission denied."); break; case EAGAIN: AddAnswerBoxItem(&InfoMsg,"File is locked."); break; case EEXIST: AddAnswerBoxItem(&InfoMsg,"File exists."); break; case EIO: AddAnswerBoxItem(&InfoMsg,"I/O error."); break; case EMULTIHOP: AddAnswerBoxItem(&InfoMsg,"Multiple remote machines link."); break; case EMFILE: AddAnswerBoxItem(&InfoMsg,"No file descriptor."); break; case ENFILE: AddAnswerBoxItem(&InfoMsg,"File table is full."); break; case ENOLINK: AddAnswerBoxItem(&InfoMsg,"Remote system unavailible."); break; case ENOMEM: AddAnswerBoxItem(&InfoMsg,"Out of memory."); break; case ENOSPC: AddAnswerBoxItem(&InfoMsg,"Out of inodes."); break; case ENOTDIR: AddAnswerBoxItem(&InfoMsg,"Invalid file specification."); break; case ENXIO: AddAnswerBoxItem(&InfoMsg,"Stream device failure."); break; case EROFS: AddAnswerBoxItem(&InfoMsg,"File system is read-only."); break; case ETXTBSY: AddAnswerBoxItem(&InfoMsg,"Text file busy."); break; default: sprintf(DummyStr,"Unknown reason (errno %d).",errno); AddAnswerBoxItem(&InfoMsg,DummyStr); break; } InfoBox(ErrorNorm,ErrorInv," Find File ",InfoMsg,1,0,DOUBLE_FRAMED); break; } for(Temp = Find; Temp != (Find_t *)NULL; Temp = Temp->Suivant) { strcpy(DummyStr,Temp->Ele.Path); if(DummyStr[strlen(DummyStr) - 1] != PATH_SEPARATOR_CHAR) strcat(DummyStr,PATH_SEPARATOR_STR); strcat(DummyStr,Temp->Ele.FileName); fprintf(hf,"%s\n",DummyStr); } fclose(hf); InitAnswerBoxItem(&InfoMsg); AddAnswerBoxItem(&InfoMsg,"The search list is saved."); InfoBox(OpOKNorm,OpOKInv," Find File ",InfoMsg,1,0,DOUBLE_FRAMED); break; case CR: /* Changer de ss-rep */ /* Tester l'accessibilite */ if((DummyDir = opendir(CurrentFind->Ele.Path)) == (DIR *)NULL) { InitAnswerBoxItem(&InfoMsg); AddAnswerBoxItem(&InfoMsg, " Sorry, impossible to access the directory "); AddAnswerBoxItem(&InfoMsg,CurrentFind->Ele.Path); InfoBox(ErrorNorm,ErrorInv," Change Directory ",InfoMsg,0,0, DOUBLE_FRAMED); } else { closedir(DummyDir); CloseWin(); chdir(CurrentFind->Ele.Path); free(Current[AL]->Info.Name); Current[AL]->Info.Name = (char *)malloc( strlen(CurrentFind->Ele.FileName) + 2); if(Current[AL]->Info.Name == (char *)NULL) raise(SIGME); if(strstr(CurrentFind->Ele.DispLine,"Directory") == (char *)NULL) strcpy(Current[AL]->Info.Name,CurrentFind->Ele.FileName); else { strcpy(Current[AL]->Info.Name,PATH_SEPARATOR_STR); strcat(Current[AL]->Info.Name,CurrentFind->Ele.FileName); } Fini = 1; rc = 1; } break; case DELETE: case CTRL_C: case ESC: /* Quitter sans rien faire */ CloseWin(); Fini = 1; rc = 0; break; case K_UP: if(CurrentFind->Prec != (Find_t *)NULL) { /* Si le choix courant a un predecesseur, y deplacer la barre avec scrolling bas si necessaire */ SelIndex--; PaintString(InvLig,InvCol,ActLength,FindFileNorm); CurrentFind = CurrentFind->Prec; if(InvLig == ActLig) { ScrollDownWin(ActLig,ActCol,ActHeight,ActLength,1); WriteString(CurrentFind->Ele.DispLine,FindFileNorm,ActLig,ActCol); } else InvLig--; PaintString(InvLig,InvCol,ActLength,FindFileInv); } break; case K_DOWN: /* Si le choix courant a un suivant, y deplacer la barre avec scrolling haut si necessaire */ if(CurrentFind->Suivant != (Find_t *)NULL) { SelIndex++; /* Si le choix courant a un suivant, y deplacer la barre avec scrolling haut si necessaire */ PaintString(InvLig,InvCol,ActLength,FindFileNorm); CurrentFind = CurrentFind->Suivant; if(InvLig == ActLig + ActHeight - 1) { ScrollUpWin(ActLig,ActCol,ActHeight,ActLength,1); WriteString(CurrentFind->Ele.DispLine,FindFileNorm, ActLig+ActHeight-1,ActCol); } else InvLig++; PaintString(InvLig,InvCol,ActLength,FindFileInv); } break; case K_HOME: case CTRL_B: /* Si on est pas deja sur la premiere option, s'y deplacer, avec mise a jour de la porion de liste affichee si necessaire */ if(SelIndex != 0) { if(SelIndex < ActHeight && InvLig - SelIndex >= ActLig) /* L'item 0 est tjs dans la fenetre: simplement deplacer la barre */ PaintString(InvLig,InvCol,ActLength,FindFileNorm); else { /* L'item 0 n'est plus dans la fenetre: effacer tous les items et les reafficher a partir du 0, puis y placer la barre */ ClrZone(ActLig,ActCol,ActHeight,ActLength,FindFileNorm); for(i = ActLig,CurrentFind = Find; i < (ActLig + ActHeight) && CurrentFind != (Find_t *)NULL; i++,CurrentFind = CurrentFind->Suivant) WriteString(CurrentFind->Ele.DispLine,FindFileNorm,i,ActCol); } CurrentFind = Find; SelIndex = 0; InvLig = ActLig; PaintString(InvLig,InvCol,ActLength,FindFileInv); } break; case K_END: case CTRL_E: /* Si on est pas sur la derniere option, s'y placer, avec mise a jour de la porion de liste affichee si necessaire */ if(SelIndex != NbreEntries - 1) { if(InvLig + NbreEntries - SelIndex <= ActLig + ActHeight) /* Le dernier item est deja dans la fenetre: simplement deplacer la barre */ PaintString(InvLig,InvCol,ActLength,FindFileNorm); else { /* Le dernier item n'est pas dans la fenetre: effacer tout et les reafficher pour avoir le dernier item en derniere position de la fenetre */ ClrZone(ActLig,ActCol,ActHeight,ActLength,FindFileNorm); /* Positionner CurrentFind sur le premier item a devoir etre reaffiche en remontant la liste a partir de la fin */ for(i = 0,CurrentFind = EndFind; i < ActHeight - 1 && CurrentFind != (Find_t *)NULL; i++,CurrentFind = CurrentFind->Prec); /* Affichage */ for(i = ActLig; i < (ActLig + ActHeight) && CurrentFind != (Find_t *)NULL; i++,CurrentFind = CurrentFind->Suivant) WriteString(CurrentFind->Ele.DispLine,FindFileNorm,i,ActCol); } CurrentFind = EndFind; SelIndex = NbreEntries - 1; if(NbreEntries < ActHeight) InvLig = ActLig + SelIndex; else InvLig = ActLig + ActHeight - 1; PaintString(InvLig,InvCol,ActLength,FindFileInv); } break; case CTRL_U: case K_PGUP: /* Si on est au milieu ou a la fin de la fenetre, remonter sur la premiere option Si on est au sommet de la fenetre, rectifier l'affichage vers le haut d'une fenetre moins un element. Si il ne reste pas assez d'elements pour faire ca, reafficher depuis le debut et placer la barre sur le premier element */ if(SelIndex != 0) { PaintString(InvLig,InvCol,ActLength,FindFileNorm); if(InvLig != ActLig) { /* La barre est au milieu de la fenetre: simplement la placer au sommet */ for(i=InvLig;i>ActLig;i--,CurrentFind = CurrentFind->Prec,SelIndex--); InvLig = ActLig; } else { /* La barre est deja au sommet: effacer tout et remonter dans la liste d'un nombre d'items egal a la hauteur de la fenetre. */ for(i = 0; i < ActHeight - 1 && CurrentFind != (Find_t *)NULL; i++,CurrentFind = CurrentFind->Prec,SelIndex--); /* Si CurrentFind est a NULL, c'est que ce qui restait de la liste est plus petit qu'une hauteur de fenetre: on va donc reafficher depuis le debut */ if(CurrentFind == (Find_t *)NULL) { CurrentFind = Find; SelIndex = 0; } /* Affichage */ Temp = CurrentFind; ClrZone(ActLig,ActCol,ActHeight,ActLength,FindFileNorm); for(i = ActLig; i < (ActLig + ActHeight) && Temp != (Find_t *)NULL; i++,Temp = Temp->Suivant) WriteString(Temp->Ele.DispLine,FindFileNorm,i,ActCol); InvLig = ActLig; } PaintString(InvLig,InvCol,ActLength,FindFileInv); } break; case CTRL_D: case K_PGDN: /* Idem PgUp, mais vers le bas */ if(SelIndex != NbreEntries - 1) { PaintString(InvLig,InvCol,ActLength,FindFileNorm); if(InvLig != ActLig + ActHeight - 1) { /* La barre est au milieu de la fenetre: simplement la placer a la fin */ if(NbreEntries < ActHeight) { /* La liste est plus petite que la fenetre */ CurrentFind = EndFind; InvLig = ActLig + NbreEntries - 1; SelIndex = NbreEntries - 1; } else { /* La liste est plus grande que la fenetre */ for(i=InvLig;i < ActLig + ActHeight - 1; i++,CurrentFind = CurrentFind->Suivant,SelIndex++); InvLig = ActLig + ActHeight - 1; } } else { /* La barre est deja a la fin: effacer tout et redescendre dans la liste d'un nombre d'items egal a la hauteur de la fenetre. - Temp memorisera le premier item a etre reaffiche, c-a-d celui qui sera au sommet de la fenetre - CurrentFind memorisera celui ou la barre en inverse se trouvera, dans ce cas, toujours sur la derniere ligne de la fenetre */ /* Donc, si il reste plus qu'une hauteur de fenetre dans la liste, lors d'un PGDN, l'item qui etait en inverse lorsque la barre est en fin de fenetre sera celui qui commencera les affichage a la fenetre suivante */ Temp = CurrentFind; /* Tester si il reste plus d'un fenetre a afficher */ for(i = 0; i < ActHeight - 1 && CurrentFind != (Find_t *)NULL; i++,CurrentFind = CurrentFind->Suivant,SelIndex++); /* Si CurrentFind est a NULL, c'est que ce qui restait de la liste est plus petit qu'une hauteur de fenetre: on va donc reafficher une fenetre ayant le dernier item sur la derniere ligne */ if(CurrentFind == (Find_t *)NULL) { /* Positionner Temp sur le premier item a devoir etre reaffiche en remontant la liste a partir de la fin. CurrentFind sera dans ce cas toujour le dernier item de la liste */ for(i = 0,CurrentFind = EndFind,Temp = EndFind, SelIndex = NbreEntries - 1; i < ActHeight - 1 && Temp != (Find_t *)NULL; i++,Temp = Temp->Prec); } /* Affichage */ ClrZone(ActLig,ActCol,ActHeight,ActLength,FindFileNorm); for(i = ActLig; i < (ActLig + ActHeight) && Temp != (Find_t *)NULL; i++,Temp = Temp->Suivant) WriteString(Temp->Ele.DispLine,FindFileNorm,i,ActCol); InvLig = ActLig + ActHeight - 1; } PaintString(InvLig,InvCol,ActLength,FindFileInv); } break; } } /* Liberer l'arbre */ CurrentFind = Find; Dummy = (Find_t *)NULL; while(CurrentFind != (Find_t *)NULL) { Dummy = CurrentFind; CurrentFind = CurrentFind->Suivant; free(Dummy->Ele.Path); free(Dummy->Ele.FileName); free(Dummy); } Find = (Find_t *)NULL; return(rc); } /***************************************************************************** SearchToFind() ------------------------------------------------------------------------------ Input: - masque de recherche - ss-rep de depart Process: fonction recursive de recherche. Insertion en tete. Output: / *****************************************************************************/ int SearchToFind(char *SearchMask,char *SearchStart) { DIR *fd; char Path[FILENAME_LENGTH],Path2[FILENAME_LENGTH]; DirList_t *List = (DirList_t *)NULL, *Tmp; Find_t *New = (Find_t *)NULL; int i,rc,X,Y; /* Boite de dialogue */ AnswerBoxItem_t *Msg = (AnswerBoxItem_t *)NULL, *Choices = (AnswerBoxItem_t *)NULL; /* Interruption utilisateur ? */ if(KeyPressed()) { GetXY(&X,&Y); GotoXY(1,1); ReadKbd(); InitAnswerBoxItem(&Msg); InitAnswerBoxItem(&Choices); AddAnswerBoxItem(&Msg,"Operation interrupted by user."); AddAnswerBoxItem(&Msg," Do you wish to cancel the search? "); AddAnswerBoxItem(&Choices,"Cancel"); AddAnswerBoxItem(&Choices,"Resume"); rc = AnswerBox(ConfirmNorm,ConfirmInv," Find File ",Msg,Choices, 0,0,DOUBLE_FRAMED); InitAnswerBoxItem(&Msg); InitAnswerBoxItem(&Choices); GotoXY(X,Y); if(rc == 1) return(-1); } SubDirCount++; SetCurrentAtt(DoingNorm); GotoXY(12,40); printf("%d",SubDirCount); fflush(stdout); strcpy(Path,SearchStart); fd = opendir(Path); if(fd == (DIR *)NULL) return(0); else closedir(fd); if(Path[strlen(Path) - 1] != PATH_SEPARATOR_CHAR) strcat(Path,PATH_SEPARATOR_STR); strcat(Path,"*.*"); if(Dir(Path,&List) == 0) return(0); Tmp = List; while(Tmp != (DirList_t *)NULL) { if(strcmp(Tmp->Info.Name,".") != 0 && strcmp(Tmp->Info.Name,"..") != 0) { if(IS_DIR(Tmp->Info.s.st_mode)) { strcpy(Path2,SearchStart); if(Path2[strlen(Path2) - 1] != PATH_SEPARATOR_CHAR) strcat(Path2,PATH_SEPARATOR_STR); strcat(Path2,(Tmp->Info.Name) + 1); if(SearchToFind(SearchMask,Path2) == -1) { FreeAllTheList(&List); return(-1); } if(IsInMask(SearchMask,(Tmp->Info.Name) + 1)) { New = (Find_t *)malloc(sizeof(Find_t)); if(New == (Find_t *)NULL) raise(SIGME); New->Ele.Path = (char *)malloc(strlen(SearchStart) + 1); if(New->Ele.Path == (char *)NULL) raise(SIGME); strcpy(New->Ele.Path,SearchStart); New->Ele.FileName = (char *)malloc(strlen(Tmp->Info.Name) + 1); if(New->Ele.FileName == (char *)NULL) raise(SIGME); strcpy(New->Ele.FileName,(Tmp->Info.Name) + 1); strcpy(Path,SearchStart); if(Path[strlen(Path) - 1] != PATH_SEPARATOR_CHAR) strcat(Path,PATH_SEPARATOR_STR); strcat(Path,(Tmp->Info.Name) + 1); if(strlen(Path) < 53) strcpy(DummyStr,Path); else { strcpy(DummyStr,"/..."); strcat(DummyStr,Path + (strlen(Path) - 48)); } strcpy(New->Ele.DispLine,DummyStr); for(i=strlen(New->Ele.DispLine);i<56;i++) strcat(New->Ele.DispLine," "); strcat(New->Ele.DispLine,"Directory"); New->Prec = (Find_t *)NULL; if(Find == (Find_t *)NULL) { New->Suivant = (Find_t *)NULL; Find = New; } else { New->Suivant = Find; Find->Prec = New; Find = New; } FindCount++; } } else { if(IsInMask(SearchMask,Tmp->Info.Name)) { New = (Find_t *)malloc(sizeof(Find_t)); if(New == (Find_t *)NULL) raise(SIGME); New->Ele.Path = (char *)malloc(strlen(SearchStart) + 1); if(New->Ele.Path == (char *)NULL) raise(SIGME); strcpy(New->Ele.Path,SearchStart); New->Ele.FileName = (char *)malloc(strlen(Tmp->Info.Name) + 1); if(New->Ele.FileName == (char *)NULL) raise(SIGME); strcpy(New->Ele.FileName,Tmp->Info.Name); strcpy(Path,SearchStart); if(Path[strlen(Path) - 1] != PATH_SEPARATOR_CHAR) strcat(Path,PATH_SEPARATOR_STR); strcat(Path,Tmp->Info.Name); if(strlen(Path) < 53) strcpy(DummyStr,Path); else { strcpy(DummyStr,"/..."); strcat(DummyStr,Path + (strlen(Path) - 48)); } strcpy(New->Ele.DispLine,DummyStr); for(i=strlen(New->Ele.DispLine);i<56;i++) strcat(New->Ele.DispLine," "); if(IS_FIFO(Tmp->Info.s.st_mode)) strcat(New->Ele.DispLine,"Pipe"); else if(IS_SCHR(Tmp->Info.s.st_mode)) strcat(New->Ele.DispLine,"Dev Char"); else if(IS_SBLK(Tmp->Info.s.st_mode)) strcat(New->Ele.DispLine,"Dev Block"); else if(IS_ORD(Tmp->Info.s.st_mode)) strcat(New->Ele.DispLine, "Ordinary"); else if(IS_SYMLINK(Tmp->Info.s.st_mode)) strcat(New->Ele.DispLine,"Symb Link"); else if(IS_SOCKET(Tmp->Info.s.st_mode)) strcat(New->Ele.DispLine,"Socket"); else strcat(New->Ele.DispLine,"Unknown"); New->Prec = (Find_t *)NULL; if(Find == (Find_t *)NULL) { New->Suivant = (Find_t *)NULL; Find = New; } else { New->Suivant = Find; Find->Prec = New; Find = New; } FindCount++; } } } Tmp = Tmp->Suivant; } FreeAllTheList(&List); return(0); }