More cleanups of dead code
[notion.git] / utils / ion-completefile / ion-completefile.c
blob6e8111c86c79ba4351ef25eecd652043f5f654b7
1 /*
2 * ion/share/ion-completefile/ion-completefile.c
3 */
5 /****************************************************************************/
6 /* */
7 /* Copyright 1992 Simmule Turner and Rich Salz. All rights reserved. */
8 /* */
9 /* This software is not subject to any license of the American Telephone */
10 /* and Telegraph Company or of the Regents of the University of California. */
11 /* */
12 /* Permission is granted to anyone to use this software for any purpose on */
13 /* any computer system, and to alter it and redistribute it freely, subject */
14 /* to the following restrictions: */
15 /* 1. The authors are not responsible for the consequences of use of this */
16 /* software, no matter how awful, even if they arise from flaws in it. */
17 /* 2. The origin of this software must not be misrepresented, either by */
18 /* explicit claim or by omission. Since few users ever read sources, */
19 /* credits must appear in the documentation. */
20 /* 3. Altered versions must be plainly marked as such, and must not be */
21 /* misrepresented as being the original software. Since few users */
22 /* ever read sources, credits must appear in the documentation. */
23 /* 4. This notice may not be removed or altered. */
24 /* */
25 /****************************************************************************/
26 /* */
27 /* This is a line-editing library, it can be linked into almost any */
28 /* program to provide command-line editing and recall. */
29 /* */
30 /* Posted to comp.sources.misc Sun, 2 Aug 1992 03:05:27 GMT */
31 /* by rsalz@osf.org (Rich $alz) */
32 /* */
33 /****************************************************************************/
34 /* */
35 /* The version contained here has some modifications by awb@cstr.ed.ac.uk */
36 /* (Alan W Black) in order to integrate it with the Edinburgh Speech Tools */
37 /* library and Scheme-in-one-defun in particular. All modifications to */
38 /* to this work are continued with the same copyright above. That is */
39 /* This version editline does not have the the "no commercial use" */
40 /* restriction that some of the rest of the EST library may have */
41 /* awb Dec 30 1998 */
42 /* */
43 /****************************************************************************/
44 /* $Revision: 1.2 $
46 ** History and file completion functions for editline library.
51 * Adapted for use with Ion and tilde-expansion added by
52 * Tuomo Valkonen <tuomov@cc.tut.fi>, 2000-08-23.
55 #include <sys/param.h>
56 #include <sys/types.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <dirent.h>
60 #include <string.h>
61 #include <unistd.h>
62 #include <sys/stat.h>
63 #include <pwd.h>
64 /* needed for NGROUPS_MAX on Solaris 10 */
65 #include <limits.h>
67 #include <libtu/util.h>
68 #include <libtu/types.h>
69 #include <libtu/misc.h>
70 #include <libtu/output.h>
72 #define STRDUP(X) scopy(X)
73 #define DISPOSE(X) free(X)
74 #define NEW(T, X) ALLOC_N(T, X)
75 #define STATIC static
76 #define SIZE_T int
77 #define MEM_INC 64
78 #define COPYFROMTO(new, p, len) \
79 (void)memcpy((char *)(new), (char *)(p), (int)(len))
81 #ifndef NGROUPS
82 /* Hopefully one of these is defined... */
83 #define NGROUPS NGROUPS_MAX
84 #endif
86 typedef struct dirent DIRENTRY;
88 static int el_is_directory(char *path)
90 struct stat sb;
92 if ((stat(path, &sb) >= 0) && S_ISDIR(sb.st_mode))
93 return 1;
94 else
95 return 0;
98 static int el_is_executable(char *path)
100 unsigned int t_uid = geteuid();
101 gid_t t_gids[NGROUPS];
102 int groupcount;
103 struct stat sb;
104 int i;
106 groupcount = getgroups(NGROUPS, t_gids);
108 if((stat(path, &sb) >= 0) && S_ISREG(sb.st_mode)) {
109 /* Normal file, see if we can execute it. */
111 if (sb.st_mode & S_IXOTH) { /* All can execute */
112 return (1);
114 if (sb.st_uid == t_uid && (sb.st_mode & S_IXUSR)) {
115 return (1);
117 if (sb.st_mode & S_IXGRP) {
118 for (i = 0; i < groupcount; i++) {
119 if (sb.st_gid == t_gids[i]) {
120 return (1);
125 return (0);
129 ** Fill in *avp with an array of names that match file, up to its length.
130 ** Ignore . and .. .
132 static int FindMatches(char *dir,char *file,char ***avp)
134 char **av;
135 char **new;
136 char *p;
137 DIR *dp;
138 DIRENTRY *ep;
139 SIZE_T ac;
140 SIZE_T len;
142 if(*dir=='\0')
143 dir=".";
145 if ((dp = opendir(dir)) == NULL)
146 return 0;
148 av = NULL;
149 ac = 0;
150 len = strlen(file);
151 while ((ep = readdir(dp)) != NULL) {
152 p = ep->d_name;
153 if (p[0] == '.' && (p[1] == '\0' || (p[1] == '.' && p[2] == '\0')))
154 continue;
155 if (len && strncmp(p, file, len) != 0)
156 continue;
158 if ((ac % MEM_INC) == 0) {
159 if ((new = NEW(char*, ac + MEM_INC)) == NULL)
160 break;
161 if (ac) {
162 COPYFROMTO(new, av, ac * sizeof (char **));
163 DISPOSE(av);
165 *avp = av = new;
168 if ((av[ac] = STRDUP(p)) == NULL) {
169 if (ac == 0)
170 DISPOSE(av);
171 break;
173 ac++;
176 /* Clean up and return. */
177 (void)closedir(dp);
178 /* if (ac)
179 qsort(av, ac, sizeof (char **), compare);*/
180 return ac;
185 ** Slight modification on FindMatches, to search through current PATH
188 static int FindFullPathMatches(char *file,char ***avp)
190 char **av;
191 char **new;
192 char *p;
193 char *path = getenv("PATH");
194 char t_tmp[1024];
195 char *t_path;
196 char *t_t_path;
197 DIR *dp;
198 DIRENTRY*ep;
199 SIZE_T ac;
200 SIZE_T len;
202 t_path = strdup(path);
203 t_t_path = t_path;
205 av = NULL;
206 ac = 0;
208 t_t_path = strtok(t_path, ":\n\0");
209 while (t_t_path) {
210 if ((dp = opendir(t_t_path)) == NULL) {
211 t_t_path = strtok(NULL, ":\n\0");
212 continue;
215 len = strlen(file);
216 while ((ep = readdir(dp)) != NULL) {
217 p = ep->d_name;
218 if (p[0] == '.' && (p[1] == '\0' || (p[0] == '.' &&
219 p[1] == '.' &&
220 p[2] == '\0'))) {
221 continue;
223 if (len && strncmp(p, file, len) != 0) {
224 continue;
227 snprintf(t_tmp, 1024, "%s/%s", t_t_path, p);
228 if(!el_is_executable(t_tmp)) {
229 continue;
232 if ((ac % MEM_INC) == 0) {
233 if ((new = NEW(char*, ac + MEM_INC)) == NULL) {
234 break;
236 if (ac) {
237 COPYFROMTO(new, av, ac * sizeof (char **));
238 DISPOSE(av);
240 *avp = av = new;
242 if ((av[ac] = STRDUP(p)) == NULL) {
243 if (ac == 0)
244 DISPOSE(av);
245 break;
247 ac++;
249 (void)closedir(dp);
250 t_t_path = strtok(NULL, ":\n\0");
253 } /* t_path-while */
255 /* Clean up and return. */
257 /*if (ac)
258 qsort(av, ac, sizeof (char **), compare); */
259 free(t_path);
261 return ac;
266 ** Split a pathname into allocated directory and trailing filename parts.
268 STATIC int SplitPath(const char *path,char **dirpart,char **filepart)
270 static char DOT[] = "./";
271 char *dpart;
272 char *fpart;
274 if ((fpart = strrchr(path, '/')) == NULL) {
275 /* No slashes in path */
276 if ((dpart = STRDUP(DOT)) == NULL)
277 return -1;
278 if ((fpart = STRDUP(path)) == NULL) {
279 DISPOSE(dpart);
280 return -1;
282 }else{
283 if ((dpart = STRDUP(path)) == NULL)
284 return -1;
285 /* Include the slash -- Tuomo */
286 dpart[fpart - path + 1] = '\0';
287 if ((fpart = STRDUP(++fpart)) == NULL) {
288 DISPOSE(dpart);
289 return -1;
291 /* Root no longer a special case due above -- Tuomo
292 if (dpart[0] == '\0')
294 dpart[0] = '/';
295 dpart[1] = '\0';
299 *dirpart = dpart;
300 *filepart = fpart;
301 return 0;
305 ** Split a pathname into allocated directory and trailing filename parts.
307 STATIC int SplitRelativePath(const char *path,char **dirpart,char **filepart)
309 static char EOL[] = "\0";
310 char *dpart;
311 char *fpart;
313 if ((fpart = strrchr(path, '/')) == NULL) {
314 /* No slashes in path */
315 if ((dpart = STRDUP(EOL)) == NULL)
316 return -1;
317 if ((fpart = STRDUP(path)) == NULL) {
318 DISPOSE(dpart);
319 return -1;
322 else {
323 if ((dpart = STRDUP(path)) == NULL)
324 return -1;
325 /* Include the slash -- Tuomo */
326 dpart[fpart - path + 1] = '\0';
327 if ((fpart = STRDUP(++fpart)) == NULL) {
328 DISPOSE(dpart);
329 return -1;
331 /* Root no longer a special case due above -- Tuomo
332 if (dpart[0] == '\0')
334 dpart[0] = '/';
335 dpart[1] = '\0';
339 *dirpart = dpart;
340 *filepart = fpart;
341 return 0;
344 static int complete_homedir(const char *username, char ***cp_ret, char **beg)
346 struct passwd *pw;
347 char *name;
348 char **cp;
349 int n=0, l=strlen(username);
351 *cp_ret=NULL;
353 for(pw=getpwent(); pw!=NULL; pw=getpwent()){
354 name=pw->pw_name;
356 if(l && strncmp(name, username, l))
357 continue;
359 name=scat3("~", name, "/");
361 if(name==NULL){
362 warn_err();
363 continue;
366 cp=REALLOC_N(*cp_ret, char*, n, n+1);
368 if(cp==NULL){
369 warn_err();
370 free(name);
371 if(*cp_ret!=NULL)
372 free(*cp_ret);
373 }else{
374 cp[n]=name;
375 n++;
376 *cp_ret=cp;
380 endpwent();
382 if(n==1){
383 name=**cp_ret;
384 name[strlen(name)-1]='\0';
385 pw=getpwnam(name+1);
386 if(pw!=NULL && pw->pw_dir!=NULL){
387 name=scat(pw->pw_dir, "/");
388 if(name!=NULL){
389 free(**cp_ret);
390 **cp_ret=name;
395 return n;
399 * ret: 0 not a home directory,
400 * 1 home directory (repath set)
401 * 2 someone's home directory
403 static int tilde_complete(char *path, char **retpath)
405 char *home;
406 char *p;
407 struct passwd *pw;
409 if(*path!='~')
410 return 0;
412 if(*(path+1)!='/' && *(path+1)!='\0'){
413 p=strchr(path, '/');
415 if(p==NULL)
416 return 2;
418 *p='\0';
419 pw=getpwnam(path+1);
420 *p='/';
422 if(pw==NULL)
423 return 0;
425 home=pw->pw_dir;
426 }else{
427 p=path+1;
428 home=getenv("HOME");
431 if(home!=NULL){
432 if(*p=='\0')
433 *retpath=scat3(home, p, "/");
434 else
435 *retpath=scat(home, p);
438 return (*retpath!=NULL);
443 ** Return all possible completions.
445 int do_complete_file(char *pathname, char ***avp, char **beg,
446 void *unused)
448 char *dir;
449 char *file, *path=NULL, *tt;
450 int ac=0, i;
452 switch(tilde_complete(pathname, &path)){
453 case 0:
454 i=SplitPath(pathname, &dir, &file);
455 break;
456 case 2:
457 return complete_homedir(pathname+1, avp, beg);
458 default:
459 i=SplitPath(path, &dir, &file);
462 if(i<0)
463 return 0;
465 ac=FindMatches(dir, file, avp);
467 DISPOSE(file);
469 if(ac==0 && path!=NULL){
470 *avp=ALLOC(char*);
471 if(*avp==NULL)
472 return 0;
473 **avp=path;
474 return 1;
475 }else if(path!=NULL){
476 free(path);
479 /* Identify directories with trailing / */
480 for(i=0; i<ac; i++){
481 path = NEW(char,strlen(dir)+strlen((*avp)[i])+3);
482 sprintf(path,"%s/%s",dir,(*avp)[i]);
483 if(el_is_directory(path)){
484 tt = NEW(char,strlen((*avp)[i])+2);
485 sprintf(tt,"%s/",(*avp)[i]);
486 DISPOSE((*avp)[i]);
487 (*avp)[i] = tt;
489 DISPOSE(path);
492 *beg=dir;
494 return ac;
499 ** Return all possible completions.
501 int do_complete_file_with_path(char *pathname, char ***avp, char **beg,
502 void *unused)
504 char *dir;
505 char *file, *path=NULL, *tt;
506 int ac=0, i, dcomp=FALSE;
508 switch(tilde_complete(pathname, &path)){
509 case 0:
510 i=SplitRelativePath(pathname, &dir, &file);
511 break;
512 case 2:
513 return complete_homedir(pathname+1, avp, beg);
514 default:
515 i=SplitPath(path, &dir, &file);
518 if(i<0)
519 return 0;
521 if(*dir=='\0')
522 ac=FindFullPathMatches(file, avp); /* No slashes in path so far. */
524 if(ac==0){
525 if(*dir=='\0'){
526 dir=scopy("./");
527 if(dir==NULL){
528 warn_err();
529 return 0;
532 ac=FindMatches(dir, file, avp);
533 dcomp=TRUE;
536 DISPOSE(file);
538 if(ac==0 && path!=NULL){
539 *avp=ALLOC(char*);
540 if(*avp==NULL)
541 return 0;
542 **avp=path;
543 return 1;
544 }else if(path!=NULL){
545 free(path);
548 /* Identify directories with trailing / */
549 if(dcomp){
550 for (i = 0; i < ac; i++) {
551 path = NEW(char,strlen(dir)+strlen((*avp)[i])+3);
552 sprintf(path,"%s/%s",dir,(*avp)[i]);
553 if (el_is_directory(path)) {
554 tt = NEW(char,strlen((*avp)[i])+2);
555 sprintf(tt,"%s/",(*avp)[i]);
556 DISPOSE((*avp)[i]);
557 (*avp)[i] = tt;
559 DISPOSE(path);
563 *beg=dir;
565 return ac;
570 int main(int argc, char *argv[])
572 char **avp=NULL;
573 char *beg=NULL;
574 bool wp=FALSE;
575 int i, j, n;
577 libtu_init(argv[0]);
579 for(i=1; i<argc; i++){
580 if(strcmp(argv[i], "-h")==0){
581 printf("Usage: ion-completefile [-help] [-wp] [to_complete...]\n");
582 return EXIT_SUCCESS;
586 for(i=1; i<argc; i++){
587 if(strcmp(argv[i], "-wp")==0){
588 wp=TRUE;
589 }else{
590 if(wp){
591 n=do_complete_file_with_path(argv[i], &avp, &beg, NULL);
592 }else{
593 n=do_complete_file(argv[i], &avp, &beg, NULL);
596 if(beg){
597 printf("%s\n", beg);
598 free(beg);
599 beg=NULL;
600 }else{
601 printf("\n");
605 if(avp){
606 for(j=0; j<n; j++){
607 printf("%s\n", avp[j]);
608 free(avp[j]);
610 free(avp);
611 avp=NULL;
616 return EXIT_SUCCESS;