complete.c 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  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, nmatch, 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. return nil;
  54. /* find longest string, for allocation */
  55. len = 0;
  56. for(i=0; i<n; i++){
  57. l = strlen(dirp[i].name) + 1 + 1; /* +1 for / +1 for \0 */
  58. if(l > len)
  59. len = l;
  60. }
  61. name = malloc(n*sizeof(char*));
  62. mode = malloc(n*sizeof(ulong));
  63. c = malloc(sizeof(Completion) + len);
  64. if(name == nil || mode == nil || c == nil)
  65. goto Return;
  66. memset(c, 0, sizeof(Completion));
  67. /* find the matches */
  68. len = strlen(s);
  69. nmatch = 0;
  70. minlen = 1000000;
  71. for(i=0; i<n; i++)
  72. if(strncmp(s, dirp[i].name, len) == 0){
  73. name[nmatch] = dirp[i].name;
  74. mode[nmatch] = dirp[i].mode;
  75. if(minlen > strlen(dirp[i].name))
  76. minlen = strlen(dirp[i].name);
  77. nmatch++;
  78. }
  79. if(nmatch > 0) {
  80. /* report interesting results */
  81. /* trim length back to longest common initial string */
  82. for(i=1; i<nmatch; i++)
  83. minlen = longestprefixlength(name[0], name[i], minlen);
  84. /* build the answer */
  85. c->complete = (nmatch == 1);
  86. c->advance = c->complete || (minlen > len);
  87. c->string = (char*)(c+1);
  88. memmove(c->string, name[0]+len, minlen-len);
  89. if(c->complete)
  90. c->string[minlen++ - len] = (mode[0]&DMDIR)? '/' : ' ';
  91. c->string[minlen - len] = '\0';
  92. } else {
  93. /* no match, so return all possible strings */
  94. for(i=0; i<n; i++){
  95. name[i] = dirp[i].name;
  96. mode[i] = dirp[i].mode;
  97. }
  98. nmatch = n;
  99. }
  100. /* attach list of names */
  101. nbytes = nmatch * sizeof(char*);
  102. for(i=0; i<nmatch; i++)
  103. nbytes += strlen(name[i]) + 1 + 1;
  104. c->filename = malloc(nbytes);
  105. if(c->filename == nil)
  106. goto Return;
  107. p = (char*)(c->filename + nmatch);
  108. for(i=0; i<nmatch; i++){
  109. c->filename[i] = p;
  110. strcpy(p, name[i]);
  111. p += strlen(p);
  112. if(mode[i] & DMDIR)
  113. *p++ = '/';
  114. *p++ = '\0';
  115. }
  116. c->nfile = nmatch;
  117. qsort(c->filename, c->nfile, sizeof(c->filename[0]), strpcmp);
  118. Return:
  119. free(name);
  120. free(mode);
  121. free(dirp);
  122. return c;
  123. }