complete.c 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. #include <u.h>
  2. #include <libc.h>
  3. #include "complete.h"
  4. static int
  5. longestprefixlength(char *a, char *b, int n)
  6. {
  7. int i, w;
  8. Rune ra, rb;
  9. for(i=0; i<n; i+=w){
  10. w = chartorune(&ra, a);
  11. chartorune(&rb, b);
  12. if(ra != rb)
  13. break;
  14. a += w;
  15. b += w;
  16. }
  17. return i;
  18. }
  19. void
  20. freecompletion(Completion *c)
  21. {
  22. if(c){
  23. free(c->filename);
  24. free(c);
  25. }
  26. }
  27. static int
  28. strpcmp(const void *va, const void *vb)
  29. {
  30. char *a, *b;
  31. a = *(char**)va;
  32. b = *(char**)vb;
  33. return strcmp(a, b);
  34. }
  35. Completion*
  36. complete(char *dir, char *s)
  37. {
  38. long i, l, n, nfile, len, nbytes;
  39. int fd, minlen;
  40. Dir *dirp;
  41. char **name, *p;
  42. ulong* mode;
  43. Completion *c;
  44. if(strchr(s, '/') != nil){
  45. werrstr("slash character in name argument to complete()");
  46. return nil;
  47. }
  48. fd = open(dir, OREAD);
  49. if(fd < 0)
  50. return nil;
  51. n = dirreadall(fd, &dirp);
  52. if(n <= 0){
  53. close(fd);
  54. return nil;
  55. }
  56. /* find longest string, for allocation */
  57. len = 0;
  58. for(i=0; i<n; i++){
  59. l = strlen(dirp[i].name) + 1 + 1; /* +1 for / +1 for \0 */
  60. if(l > len)
  61. len = l;
  62. }
  63. name = malloc(n*sizeof(char*));
  64. mode = malloc(n*sizeof(ulong));
  65. c = malloc(sizeof(Completion) + len);
  66. if(name == nil || mode == nil || c == nil)
  67. goto Return;
  68. memset(c, 0, sizeof(Completion));
  69. /* find the matches */
  70. len = strlen(s);
  71. nfile = 0;
  72. minlen = 1000000;
  73. for(i=0; i<n; i++)
  74. if(strncmp(s, dirp[i].name, len) == 0){
  75. name[nfile] = dirp[i].name;
  76. mode[nfile] = dirp[i].mode;
  77. if(minlen > strlen(dirp[i].name))
  78. minlen = strlen(dirp[i].name);
  79. nfile++;
  80. }
  81. if(nfile > 0) {
  82. /* report interesting results */
  83. /* trim length back to longest common initial string */
  84. for(i=1; i<nfile; i++)
  85. minlen = longestprefixlength(name[0], name[i], minlen);
  86. /* build the answer */
  87. c->complete = (nfile == 1);
  88. c->advance = c->complete || (minlen > len);
  89. c->string = (char*)(c+1);
  90. memmove(c->string, name[0]+len, minlen-len);
  91. if(c->complete)
  92. c->string[minlen++ - len] = (mode[0]&DMDIR)? '/' : ' ';
  93. c->string[minlen - len] = '\0';
  94. c->nmatch = nfile;
  95. } else {
  96. /* no match, so return all possible strings */
  97. for(i=0; i<n; i++){
  98. name[i] = dirp[i].name;
  99. mode[i] = dirp[i].mode;
  100. }
  101. nfile = n;
  102. c->nmatch = 0;
  103. }
  104. /* attach list of names */
  105. nbytes = nfile * sizeof(char*);
  106. for(i=0; i<nfile; i++)
  107. nbytes += strlen(name[i]) + 1 + 1;
  108. c->filename = malloc(nbytes);
  109. if(c->filename == nil)
  110. goto Return;
  111. p = (char*)(c->filename + nfile);
  112. for(i=0; i<nfile; i++){
  113. c->filename[i] = p;
  114. strcpy(p, name[i]);
  115. p += strlen(p);
  116. if(mode[i] & DMDIR)
  117. *p++ = '/';
  118. *p++ = '\0';
  119. }
  120. c->nfile = nfile;
  121. qsort(c->filename, c->nfile, sizeof(c->filename[0]), strpcmp);
  122. Return:
  123. free(name);
  124. free(mode);
  125. free(dirp);
  126. close(fd);
  127. return c;
  128. }