summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/doc/latex/songs/songidx/songsort.c
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/doc/latex/songs/songidx/songsort.c')
-rw-r--r--Master/texmf-dist/doc/latex/songs/songidx/songsort.c162
1 files changed, 162 insertions, 0 deletions
diff --git a/Master/texmf-dist/doc/latex/songs/songidx/songsort.c b/Master/texmf-dist/doc/latex/songs/songidx/songsort.c
new file mode 100644
index 00000000000..22825733727
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/songs/songidx/songsort.c
@@ -0,0 +1,162 @@
+/* Copyright (C) 2012 Kevin W. Hamlen
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * The latest version of this program can be obtained from
+ * http://songs.sourceforge.net.
+ */
+
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#else
+# include "vsconfig.h"
+#endif
+
+#include "chars.h"
+#include "songidx.h"
+#include "fileio.h"
+
+/* skipesc_sp(<ptr>,<flag>)
+ * Walk <ptr> past any LaTeX macros or braces until we reach the next "real"
+ * character. If <flag> is 1, then make an exception for space-macros.
+ * The space macro "\ " causes p to stop on the space. */
+void
+skipesc_sp(p, stop_on_space)
+ const WCHAR **p;
+ int stop_on_space;
+{
+ for (;;)
+ {
+ if (**p == wc_backslash)
+ {
+ ++(*p);
+ if (stop_on_space && wc_isspace(**p)) return;
+ if (wc_isalpha(**p)) while (wc_isalpha(**p)) ++(*p);
+ else if (**p) ++(*p);
+ while (wc_isspace(**p)) ++(*p);
+ }
+ else if (**p == wc_lbrace)
+ {
+ ++(*p);
+ while (wc_isspace(**p)) ++(*p);
+ }
+ else if (**p == wc_rbrace)
+ ++(*p);
+ else
+ break;
+ }
+}
+
+/* skipesc(<ptr>)
+ * Walk <ptr> past any LaTeX macros or braces until we reach the next "real"
+ * character. */
+void
+skipesc(p)
+ const WCHAR **p;
+{
+ skipesc_sp(p,0);
+}
+
+/* inword(<char>)
+ * Return 0 if <char> is a word-delimiter (for sorting purposes) and 1
+ * otherwise. Other than TeX macros (which are handled by skipesc above),
+ * words only have alphabetics and apostrophes. */
+static int
+inword(c)
+ WCHAR c;
+{
+ return (wc_isalpha(c) || (c==wc_apostrophe) || (c==wc_backquote));
+}
+
+/* songcmp(<song1>,<song2>)
+ * Return a negative number if <song1> is less than <song2>, a positive
+ * number if <song1> is greater than <song2>, and 0 if <song1> and <song2>
+ * are equal. The ordering is first by title, then by index. This function
+ * is suitable for use with qsort(). */
+int
+songcmp(s1,s2)
+ const void *s1;
+ const void *s2;
+{
+ static WCHAR buf1[MAXLINELEN+1], *bp1;
+ static WCHAR buf2[MAXLINELEN+1], *bp2;
+ const WCHAR *t1 = (*((const SONGENTRY **) s1))->title;
+ const WCHAR *t2 = (*((const SONGENTRY **) s2))->title;
+ int diff;
+
+ for (;;)
+ {
+ /* Find the next word or number in each string. */
+ skipesc(&t1);
+ while(*t1 && !wc_isalpha(*t1) && !wc_isdigit(*t1))
+ {
+ ++t1;
+ skipesc(&t1);
+ }
+ skipesc(&t2);
+ while(*t2 && !wc_isalpha(*t2) && !wc_isdigit(*t2))
+ {
+ ++t2;
+ skipesc(&t2);
+ }
+
+ /* If there is no next word/number in one or both, sort the shorter
+ * string before the longer one. */
+ if ((*t1==wc_null) || (*t2==wc_null))
+ {
+ if ((*t1==wc_null) && (*t2==wc_null)) break;
+ return (t1!=wc_null) ? 1 : -1;
+ }
+
+ /* If one is a number, sort the number before the word. If both are
+ * numbers, sort in numerical order. */
+ if (wc_isdigit(*t1) || wc_isdigit(*t2))
+ {
+ long n1, n2;
+ WCHAR *p1, *p2;
+ if (!wc_isdigit(*t1)) return 1;
+ if (!wc_isdigit(*t2)) return -1;
+ n1 = ws_strtol(t1, &p1, 10);
+ t1 = p1;
+ n2 = ws_strtol(t2, &p2, 10);
+ t2 = p2;
+ if (n1 == n2) continue;
+ return n1-n2;
+ }
+
+ /* Otherwise, both are words. Copy the words into scratch buffers,
+ * omitting any macros or braces. Then lexographically compare the buffer
+ * contents to determine how the original strings should be sorted. */
+ for (bp1=buf1; inword(*t1); skipesc_sp(&t1,1)) *bp1++=wc_tolower(*t1++);
+ for (bp2=buf2; inword(*t2); skipesc_sp(&t2,1)) *bp2++=wc_tolower(*t2++);
+ *bp1 = *bp2 = wc_null;
+ if ((diff = ws_coll(buf1,buf2)) != 0) return diff;
+ }
+
+ /* If each corresponding word/number is identical, then sort alternate-
+ * form entries (e.g., lyrics) after normal entries (e.g., titles). */
+ if (((*((const SONGENTRY **) s1))->title[0] == wc_asterisk)
+ && ((*((const SONGENTRY **) s2))->title[0] != wc_asterisk))
+ return 1;
+ if (((*((const SONGENTRY **) s1))->title[0] != wc_asterisk)
+ && ((*((const SONGENTRY **) s2))->title[0] == wc_asterisk))
+ return -1;
+
+ /* If everything is the same, sort by the right-hand sides of the index
+ * entries (e.g., the song or page numbers). */
+ return (*((const SONGENTRY **) s1))->idx - (*((const SONGENTRY **) s2))->idx;
+}