qsort.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. /** 2022.10.04 this includes exercize 5-15/5-16 working on 5-17 **/
  5. /**
  6. errata
  7. undefined behaviour when you use -n on non numeric data
  8. */
  9. #define MAXLINES 20000 /* max #lines to be sorted */
  10. // int readlines(char *lineptr[], int nlines);
  11. // void writelines(char *lineptr[], int nlines);
  12. #define MAXLEN 1000 /* max length of any input line */
  13. #define ERR_TOOBIG 1 /** too big */
  14. #define ARGUMENT_PROBLEM 2 /** something wrong in commandline arguments given */
  15. int getline1(char *, int);
  16. char *lineptr[MAXLINES]; /* pointers to text lines */
  17. int readlines(char *lineptr[], int nlines);
  18. void writelines(char *lineptr[], int nlines);
  19. void swap(void *v[], int, int);
  20. void qsort1(void *v[], int left, int right, int noSort, int caseInsensitive, int directory,
  21. int (*comp)(void *, void *, int, int));
  22. int numcmp(char *, char *, int, int);
  23. int isAlphaNumeric(char);
  24. //helper function for zero()
  25. void zerome(int*);
  26. //initializes an array of ints
  27. void zero (int*, int);
  28. //helper function to reverse an array v
  29. // why hasn't this got a function prototype? @TODO
  30. void reverse(void *v[], int left, int right)
  31. {
  32. int i, last;
  33. if (left >= right)
  34. return;
  35. swap(v,left,right);
  36. reverse(v,left+1,right-1);
  37. }
  38. void zerome(int* c)
  39. {
  40. *c = 0;
  41. }
  42. void zero (int* a, int len)
  43. {
  44. for (int i =0; i<len; i++)
  45. zerome( a+i );
  46. // apparently this doesn't work
  47. // zerome( a+sizeof(i)*i );
  48. }
  49. /* sort input lines */
  50. int main(int argc, char *argv[])
  51. {
  52. int strcmp1(char *s, char *t);
  53. int strcmp2(char *s, char *t, int u, int v);
  54. int fieldcount = 1; // cardinal. we always start with 1
  55. // step 1 - figure out how many fields we're talking about here
  56. for (int i=0; i< argc; i++)
  57. if ( strcmp1(argv[i], "-l") ==0 ) fieldcount++;
  58. int help = 0; // only one help will get us help.
  59. // ok now we've got a number of fields we have to deal with
  60. // we need their positions, as to skip them
  61. //sets field[0] as 0
  62. //turns out we were one too high at fieldcount+1
  63. int field [fieldcount] ;
  64. zero(field,fieldcount);
  65. // length of fields, they can be different
  66. int fieldlen [fieldcount] ;
  67. zero(fieldlen,fieldcount);
  68. for (int i=0; i< argc; i++)
  69. {
  70. if ( strcmp1(argv[i], "-l") ==0 )
  71. {
  72. field[i+1] = i; //we need this position
  73. // @TODO needs sanity checking
  74. if ((i+1) > argc)
  75. {
  76. printf ("-l out of bounds");
  77. return ARGUMENT_PROBLEM;
  78. }
  79. fieldlen[i+1]=atoi(argv[i+1]); //is this the right format?
  80. /*
  81. -l l1 -l l2 -l l3 -l l4 -l l5
  82. -------|-------------|-|-|-------|
  83. l1 l2 l3l4 l5
  84. */
  85. }
  86. }
  87. /* flags*/
  88. int numeric[fieldcount];
  89. int backwards[fieldcount];
  90. int caseInsensitive[fieldcount];
  91. int directory[fieldcount];
  92. int noSort[fieldcount];
  93. //initialize
  94. zero(numeric,fieldcount);
  95. zero(backwards,fieldcount);
  96. zero(caseInsensitive,fieldcount) ;
  97. zero(directory,fieldcount);
  98. zero(noSort,fieldcount);
  99. /** WIP */
  100. for (int i=0, j=0; i < fieldcount; i++) //we're iterating fields here. we start with 0.
  101. {
  102. int nextfield = field[i]; //we go from j to nextfield
  103. for (; j<argc; j++) //we only iterate to the end of either argv OR the field
  104. //we are iterating on argv so argv[j] is the thing we are using
  105. {
  106. if (j==nextfield && nextfield != 0)
  107. i++;
  108. // printf("*** argc %d %s \n",argc,argv[i]);
  109. noSort[i] |= (strcmp1(argv[j], "-x") == 0);
  110. numeric[i] |= (strcmp1(argv[j], "-n") == 0);
  111. caseInsensitive[i] |= (( strcmp1(argv[j], "-f") == 0 ) || ( strcmp1(argv[j], "-df") ==0 ));
  112. backwards[i] |= ( strcmp1(argv[j], "-r") ==0 ) ;
  113. directory[i] |= (( strcmp1(argv[j], "-d") ==0 ) || ( strcmp1(argv[j], "-df") ==0 ));
  114. // we don't have to check for -l because we already did that
  115. // only one of these
  116. help |= (( strcmp1(argv[j], "-h") ==0 ) || ( strcmp1(argv[j], "--help") ==0 ));
  117. }
  118. // j++; //do we need this??
  119. }
  120. if (help)
  121. {
  122. printf ("./qsort [-l OPTIONS]* \n");
  123. printf (" OPTIONS: \n");
  124. printf (" -x : don't actually sort fields \n");
  125. printf (" -n : sort by number \n");
  126. printf (" -r : reverse (5-14) \n");
  127. printf (" -f : fold lower/upper case together (5-15) \n");
  128. printf (" -d : sort as if directory structure (5-16) \n");
  129. printf (" -fd : -f and -d (5-16) \n");
  130. printf (" -l N demarcates the next field by N \n");
  131. return(0); //bail
  132. }
  133. printf("fieldcount %d\n",fieldcount);
  134. //shotgun debugging flags
  135. for (int i = 0; i < fieldcount; i++)
  136. {
  137. printf ("numeric, %d \ncaseInsensitive %d\nbackwards %d\nnosort %d\ni %d\ndirectory %d\n\n",numeric[i],caseInsensitive[i],backwards[i],noSort[i],i,directory[i]);
  138. }
  139. int nlines = -1; /* number of input lines read */
  140. int first = 0;
  141. int last = -1;
  142. if ((nlines = readlines(lineptr, MAXLINES)) >= 0) //read lines
  143. {
  144. for (int i =0 ; i < fieldcount; i++) //is fieldcount 0 or 1 here if there's none?
  145. {
  146. first = field[i];
  147. //what happens if someone uses -l 0?
  148. if (fieldlen[0]==0) //we didn't get a -l
  149. {
  150. last = nlines-1;
  151. }
  152. else
  153. last = first+fieldlen[i];
  154. if (last == 0)
  155. last = nlines-1;
  156. //do the sort
  157. //something's wrong here on the normal sort
  158. qsort1( (void**) lineptr, first, last, noSort[i], caseInsensitive[i], directory[i],
  159. (int (*)(void*,void*,int,int)) (numeric ? numcmp : strcmp2 ) );
  160. //this part seems to work
  161. if(backwards[i])
  162. reverse((void**)lineptr, first, last);
  163. }
  164. //output
  165. writelines(lineptr, nlines);
  166. return 0;
  167. } else {
  168. printf("input %d too big to sort, contact devs with this error message \n",nlines);
  169. return ERR_TOOBIG;
  170. }
  171. }
  172. /* qsort: sort v[left]...v[right] into increasing order */
  173. void qsort1(void *v[], int left, int right, int noSort, int caseInsensitive, int directory,
  174. int (*comp)(void *, void *, int, int))
  175. {
  176. if (noSort) return; // do nothing on no sort
  177. int i, last;
  178. if (left >= right) /* do nothing if array contains */
  179. return; /* fewer than two elements */
  180. swap(v, left, (left + right)/2);
  181. last = left;
  182. for (i = left+1; i <= right; i++)
  183. if ((*comp)(v[i], v[left], caseInsensitive, directory) < 0)
  184. swap(v, ++last, i);
  185. swap(v, left, last);
  186. qsort1(v, left, last-1, noSort, caseInsensitive, directory, comp);
  187. qsort1(v, last+1, right, noSort, caseInsensitive, directory, comp);
  188. }
  189. /** includes blanks */
  190. int isAlphaNumeric(char a)
  191. {
  192. return
  193. (
  194. ( (a >= 'A') && (a <= 'Z') ) ||
  195. ( (a >= '0') && (a <= '9') ) ||
  196. ( (a >= 'a') && (a <= 'z') ) ||
  197. (a == ' ')
  198. );
  199. }
  200. /* strcmp: return <0 if s<t, 0 if s==t, >0 if s>t */
  201. int strcmp2(char *s, char *t, int caseInsensitive, int dirmode)
  202. {
  203. for ( ; *s == *t; s++, t++)
  204. if (*s == '\0')
  205. return 0;
  206. int ci= caseInsensitive;
  207. char str = *s;
  208. //if c is lowercase
  209. char stt = *t;
  210. // 4 cases
  211. // shouldn't affect dirmode since both str and stt *are both* alphanumeric here
  212. if (ci)
  213. {
  214. if (((str >= 'a') && (str <= 'z'))
  215. && ((stt >= 'a') && (stt <= 'z'))) //s and t are both lowercase
  216. return *s - *t; //compare as normal
  217. if ((str >= 'a') && (str <= 'z'))
  218. return *s - *t -32; // s is but t isn't... drop s down
  219. if ((stt >= 'a') && (stt <= 'z'))
  220. return *s - *t +32; // t is but s isn't... drop t up
  221. }
  222. if (dirmode)
  223. //check to make sure we're dealing with
  224. // assumption: treat all non alphanumeric, non space, non numbers as the same, ie we're not checking them
  225. // for 5-16
  226. {
  227. // what if there's only one? This isn't specified by the question.
  228. if (isAlphaNumeric(str) || isAlphaNumeric(stt))
  229. return *s - *t;
  230. // all other characters are equal
  231. return (char)0;
  232. }
  233. return *s - *t; //neither? then it doesn't matter
  234. }
  235. int strcmp1(char *s, char *t)
  236. {
  237. int u=1; //case insensitive desirable here?
  238. int v=0; //dirmode not needed here
  239. return strcmp2(s,t,u,v);
  240. }
  241. /* writelines: write output lines */
  242. void writelines(char *lineptr[], int nlines)
  243. {
  244. while (nlines-- > 0)
  245. printf("%s\n", *lineptr++);
  246. }
  247. /* numcmp: compare s1 and s2 numerically */
  248. int numcmp(char *s1, char *s2, int dud, int dud2)
  249. {
  250. //dud, dud2 is not used
  251. double v1, v2;
  252. v1 = atof(s1);
  253. v2 = atof(s2);
  254. if (v1 < v2)
  255. return -1;
  256. else if (v1 > v2)
  257. return 1;
  258. else
  259. return 0;
  260. }
  261. int getline1(char s[],int lim)
  262. {
  263. int c, i;
  264. for (i=0; i < lim-1 && (c=getchar())!=EOF && c!='\n'; ++i)
  265. s[i] = c;
  266. if (c == '\n') {
  267. s[i] = c;
  268. ++i;
  269. }
  270. s[i] = '\0';
  271. return i;
  272. }
  273. /* readlines: read input lines */
  274. int readlines(char *lineptr[], int maxlines)
  275. {
  276. int len, nlines;
  277. char *p, line[MAXLEN];
  278. nlines = 0;
  279. while ((len = getline1(line, MAXLEN)) > 0)
  280. {
  281. p = malloc(sizeof(char)*len);
  282. if (nlines >= maxlines || p == NULL)
  283. return -1;
  284. else {
  285. line[len-1] = '\0'; /* delete newline */
  286. strcpy(p, line);
  287. lineptr[nlines++] = p;
  288. }
  289. }
  290. return nlines;
  291. }
  292. /* swap: interchange v[i] and v[j] */
  293. void swap(void *v[], int i, int j)
  294. {
  295. void *temp;
  296. temp = v[i];
  297. v[i] = v[j];
  298. v[j] = temp;
  299. }