summaryrefslogtreecommitdiff
path: root/mklist.c
diff options
context:
space:
mode:
authorJohannes Schauer <josch@debian.org>2015-04-29 18:43:22 +0200
committerJohannes Schauer <josch@debian.org>2015-04-29 18:43:22 +0200
commit118880912438aa219d100a85ec54ac461741b3ba (patch)
tree7e94c61a00446a229b492196669f60aa56e4a594 /mklist.c
ldraw-parts (1402+ds-1) unstable; urgency=medium
* New upstream release * Replace my email address in Maintainer field by josch@debian.org * bump standards version to 3.9.6 # imported from the archive
Diffstat (limited to 'mklist.c')
-rw-r--r--mklist.c597
1 files changed, 597 insertions, 0 deletions
diff --git a/mklist.c b/mklist.c
new file mode 100644
index 000000000..09531c64c
--- /dev/null
+++ b/mklist.c
@@ -0,0 +1,597 @@
+/*
+ * mklist.c, a replacement for James Jessiman's makelist
+ * Copyright (C) 1999 Lars C. Hassing
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*****************************************************************************
+ Please do not edit this file. In stead contact Lars C. Hassing (lch@cci.dk)
+ to get your changes integrated in a future release.
+******************************************************************************/
+
+/*
+990214 lch Release v1.0
+990316 lch Release v1.1: added options + minor changes to sort algorithms
+990518 lch Release v1.2: Skip "~Moved to xxx" by default
+20000703 lch Release v1.3: Added -i and -o
+20030625 dmh Release v1.4: Ported to gcc.
+200????? ??? Release v1.5: ??? Lost track of this one...
+20090928 dmh Release v1.6beta1: Added -f -q -v -8 and allow 25 chars in filenames.
+20100706 dmh Release v1.6: Added -l and made long format the default.
+ */
+
+/* Compile with Borland Turbo C 2.0: tcc -mc -d -f -k- -N -v- -y- -wrvl -wstv
+-wucp -wnod -wpro -wuse -wsig -ncmdsrel -M src\mklist.c */
+
+
+#include "stdio.h"
+#include "stdlib.h"
+#include "ctype.h"
+#include "string.h"
+#include "alloc.h"
+#include "dir.h"
+#include "conio.h"
+#include <libgen.h>
+
+char *ProgVer = "mklist v1.6 20100706 (C) 1999-2010 Lars C. Hassing lch@ccieurope.com";
+
+int forceit = 0;
+int quiet = 0;
+
+ /*****************************************************************/
+/* Filename length compatibility stuff */
+/*****************************************************************/
+#ifndef MAX_PATH
+#define MAX_PATH 256
+#endif
+char shortfilepath[MAX_PATH];
+char shortfilename[MAX_PATH];
+
+
+#ifdef __BORLANDC__
+#define LACKS_BASENAME 1
+int numlines = 1000; /* Use small chunks for DOS */
+int namelen = 12; /* Set to 12 for 8.3 DOS compatibility. */
+#else
+#ifdef __TURBOC__
+#define LACKS_BASENAME 1
+int numlines = 1000; /* Use small chunks for DOS */
+int namelen = 12; /* Set to 12 for 8.3 DOS compatibility. */
+#else
+int numlines = 16384; /* Allocate lines in bigger chunks if not DOS. */
+int namelen = 25; /* LSC specs allow 25 chars in filename. */
+#endif
+#endif
+
+
+#ifdef _WIN32
+#include <windows.h>
+#define LACKS_BASENAME 1
+
+#else
+int GetShortPathName(char *longpath, char * shortpath, int psize)
+{
+ strncpy(shortpath, longpath, psize);
+ return(strlen(shortpath));
+}
+#endif
+
+
+#if LACKS_BASENAME
+char *basename( const char *filepath )
+{
+ char *tmpstr, *ptr;
+
+ if (filepath == NULL)
+ {
+ return NULL;
+ }
+ if ( (ptr = strrchr(filepath, '\\')) || (ptr = strrchr(filepath, '/')) )
+ {
+ /* If there isn't anything after the last separator, the result is a 0-length string */
+ tmpstr = strdup(ptr+1);
+ }
+ else
+ {
+ /* dup the string, so caller can safely free whatever we return */
+ tmpstr = strdup(filepath);
+ }
+ return tmpstr;
+}
+#endif
+
+ /*****************************************************************/
+int CmpNumber(const void *p1, const void *p2)
+{
+ char *s1 = *((char **) p1);
+ char *s2 = *((char **) p2);
+ long l1;
+ long l2;
+
+ if (isdigit(*s1) && isdigit(*s2))
+ {
+ l1 = atol(s1);
+ l2 = atol(s2);
+ if (l1 != l2)
+ return (l1 < l2 ? -1 : 1);
+ /* Numbers are equal. Be sure to make 3005.dat come before 3005-1.dat */
+ do
+ {
+ l1 = (unsigned char) *s1++;
+ if ('A' <= l1 && l1 <= 'Z')
+ l1 -= ('A' - 'a');
+ else if (l1 == '.')
+ l1 = '\0'; /* Sort dot very first */
+ l2 = (unsigned char) *s2++;
+ if ('A' <= l2 && l2 <= 'Z')
+ l2 -= ('A' - 'a');
+ else if (l2 == '.')
+ l2 = '\0'; /* Sort dot very first */
+ } while (l1 && (l1 == l2));
+
+ return ((int) (l1 - l2));
+ }
+ return (stricmp(s1, s2));
+}
+
+/*****************************************************************/
+int CmpDescription(const void *p1, const void *p2)
+{
+ char *s1 = *((char **) p1);
+ char *s2 = *((char **) p2);
+ int Res;
+
+#if 0
+ Res = stricmp(s1 + (namelen+2), s2 + (namelen+2));
+#else
+ s1 += strcspn(s1, " \t"); /* Find the beginning of whitespace. */
+ s1 += strspn(s1, " \t"); /* Skip to end of whitespace (start of description). */
+ s2 += strcspn(s2, " \t"); /* Find the beginning of whitespace. */
+ s2 += strspn(s2, " \t"); /* Skip to end of whitespace (start of description). */
+ Res = stricmp(s1, s2);
+#endif
+
+ return (Res ? Res : CmpNumber(p1, p2));
+}
+
+/*****************************************************************/
+void PressAnyKey(void)
+{
+ if (forceit)
+ return;
+ if (quiet)
+ return;
+ printf(" Press any key to continue");
+ getch();
+ printf("\n");
+}
+
+/*****************************************************************/
+void PrintUsage(void)
+{
+ printf("Options:\n");
+ printf(" -h You already figured this one out :-)\n");
+ printf(" -n Sort by Number\n");
+ printf(" -d Sort by Description\n");
+ printf(" -c Check for duplicate descriptions. \"parts.lst\" unchanged.\n");
+ printf(" -m Don't skip parts with \"~Moved to xxx\" description\n");
+ printf(" -~ Skip parts with ~ description, e.g. \"~Winch 2 x 4 x 2 Top\"\n");
+ printf(" -i <dir> input directory, default is \"PARTS\" in current directory\n");
+ printf(" -o <file> output filename, default is \"parts.lst\" in current directory\n");
+ printf(" -f Force it to finish. No prompts.\n");
+ printf(" -q Quiet mode. No warnings, and no prompts.\n");
+ printf(" -8 Use 8.3 names for compatibility.\n");
+ printf(" -t Truncating descriptions to fit in an 80 char terminal window.\n");
+ printf(" -r Ragged filename column. Size it to fit short filenames.\n");
+ printf(" -l Truncate Long descriptions at 64 chars.\n");
+ printf(" -v Print verbose info. Useful for debugging.\n");
+}
+
+ /*****************************************************************/
+int main(int argc, char **argv)
+{
+ int CheckDuplicateDescriptions;
+ int SortBy;
+ int SkipTilde;
+ int SkipMovedto;
+ char *arg;
+ int c;
+ int i;
+ int j;
+ FILE *fp;
+ struct ffblk ffb;
+ int done;
+ int Len;
+ char **Lines;
+ int maxLines;
+ int nLines;
+ int pathlen;
+ char Line[200];
+ char Dirname[MAX_PATH];
+ char Filename[MAX_PATH];
+ char OutFilename[MAX_PATH];
+ char *s;
+ char *Description;
+ char *FormattedLine;
+ unsigned long farcoreleftStart;
+ unsigned long farcoreleftEnd;
+ long FileSize;
+ struct stat statbuf;
+ int verbose;
+ int ragged;
+ int terminalsized;
+ int longDescriptions;
+
+ printf("%s\n", ProgVer);
+ printf("Replacement for James Jessiman's makelist\n");
+ printf("Call with -h to see a list of options.\n\n");
+
+ strcpy(Dirname, "PARTS"); /* Default input directory path */
+ strcpy(OutFilename, "parts.lst"); /* Default output filename */
+
+ verbose = 0;
+ ragged = 0; /* Steve wanted it ragged (1) by default */
+ terminalsized = 0; /* Steve wanted it (1) to fit in 80 chars by default */
+ longDescriptions = 1; /* Do not truncate descriptions at 64 chars by default */
+
+ CheckDuplicateDescriptions = 0;
+ SortBy = 0;
+ SkipTilde = 0;
+ SkipMovedto = 1;
+ while (--argc > 0)
+ {
+ arg = *++argv;
+ if (arg[0] == '-')
+ {
+ switch (arg[1])
+ {
+ case '?':
+ case 'h':
+ PrintUsage();
+ exit(1);
+ break;
+ case '8':
+ namelen = 12;
+ break;
+ case 'c':
+ CheckDuplicateDescriptions = 1;
+ break;
+ case 'n':
+ case 'd':
+ SortBy = arg[1];
+ break;
+ case 'm':
+ SkipMovedto = 0;
+ break;
+ case '~':
+ SkipTilde = 1;
+ break;
+ case 'i':
+ if (--argc > 0)
+ strcpy(Dirname, *++argv);
+ else
+ {
+ PrintUsage();
+ printf("*** input directory expected as next argument after -i.\n");
+ exit(1);
+ }
+ break;
+ case 'o':
+ if (--argc > 0)
+ strcpy(OutFilename, *++argv);
+ else
+ {
+ PrintUsage();
+ printf("*** output filename expected as next argument after -o.\n");
+ exit(1);
+ }
+ break;
+ case 'q':
+ quiet = 1;
+ case 'f':
+ forceit = 1;
+ break;
+ case 'r':
+ ragged = ragged ^ 1;
+ break;
+ case 't':
+ terminalsized = terminalsized ^ 1;
+ break;
+ case 'l':
+ longDescriptions = longDescriptions ^ 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ default:
+ PrintUsage();
+ printf("*** Unknown option '%s'.\n", arg);
+ exit(1);
+ break;
+ }
+ }
+ else
+ {
+ PrintUsage();
+ exit(1);
+ }
+ }
+
+ /* Do a stat to see if Dirname exists and is a directory. */
+ if (stat(Dirname, &statbuf) < 0)
+ {
+ printf("*** Could not stat input directory \"%s\".\n", Dirname);
+ exit(1);
+ }
+
+ if ((statbuf.st_mode & S_IFDIR) == 0)
+ {
+ printf("*** Input directory \"%s\" is not a directory.\n", Dirname);
+ exit(1);
+ }
+
+ if (CheckDuplicateDescriptions)
+ SortBy = 'd';
+ if (!SortBy)
+ {
+ if (forceit)
+ SortBy = 'd';
+ if (!quiet)
+ printf("Sorting by [D]escription.\n");
+ }
+
+ if (!SortBy)
+ {
+ printf("Sort by [N]umber or [D]escription: ");
+ c = getch();
+ printf("%c\n", c);
+
+ if (c == 'N' || c == 'n')
+ SortBy = 'n';
+ else if (c == 'D' || c == 'd')
+ SortBy = 'd';
+ else
+ {
+ printf("Nothing done.\n");
+ exit(0);
+ }
+ }
+
+ farcoreleftStart = farcoreleft();
+
+ nLines = 0;
+ maxLines = numlines;
+ Lines = farmalloc(maxLines * sizeof(char *));
+ if (!Lines)
+ {
+ printf("Out of memory after %d parts\n", nLines);
+ printf("Memory available at beginning: %ld kBytes\n",
+ (farcoreleftStart + 1023) / 1024);
+ exit(1);
+ }
+ strcpy(Filename, Dirname);
+ strcat(Filename, "\\");
+ pathlen = strlen(Filename);
+ strcat(Filename, "*.*");
+ for (done = findfirst(Filename, &ffb, 0); !done; done = findnext(&ffb))
+ {
+ if (verbose)
+ {
+ printf("Processing file: \"%s\"\n", ffb.ff_name);
+ }
+ strcpy(Filename + pathlen, ffb.ff_name);
+ fp = fopen(Filename, "rt");
+ if (!fp)
+ {
+ if (!quiet) printf("Cannot open \"%s\"", ffb.ff_name);
+ PressAnyKey();
+ continue;
+ }
+ fgets(Line, sizeof(Line), fp);
+ fclose(fp);
+
+ s = Line + strlen(Line) - 1;
+ while (s >= Line && (*s == '\n' || *s == '\r' || *s == '\t' || *s == ' '))
+ *s-- = '\0'; /* clear newline and trailing tabs
+ and spaces */
+ s = Line;
+ while (*s == '\t' || *s == ' ')
+ *s++;
+ if (*s++ != '0')
+ {
+ if (!quiet) printf("Line type 0 expected in \"%s\", skipping...", ffb.ff_name);
+ PressAnyKey();
+ continue;
+ }
+ while (*s == '\t' || *s == ' ')
+ *s++;
+ Description = s;
+ if (SkipTilde && Description[0] == '~')
+ continue;
+ if (SkipMovedto && strncmp(Description, "~Moved to", 9) == 0)
+ continue;
+ Len = strlen(Description);
+ if (Len == 0)
+ {
+ if (!quiet) printf("Empty description in \"%s\"", ffb.ff_name);
+ PressAnyKey();
+ }
+ if ((Len > 64) && !longDescriptions)
+ {
+ /* Original makelist truncates to 64 characters. */
+ if (!quiet)
+ {
+ printf("Description in \"%s\" will be truncated to 64 characters:\n",
+ ffb.ff_name);
+ printf("Before: \"%s\"\n", Description);
+ printf("After: \"%-64.64s\"\n", Description);
+ }
+ PressAnyKey();
+ }
+ if (namelen == 12)
+ FormattedLine = farmalloc(79);
+ else
+ FormattedLine = farmalloc(256);
+ if (!FormattedLine)
+ {
+ printf("Out of memory after %d parts\n", nLines);
+ printf("Memory available at beginning: %ld kBytes\n",
+ (farcoreleftStart + 1023) / 1024);
+ exit(1);
+ }
+
+
+ if (namelen > 12)
+ strcpy(shortfilename, ffb.ff_name);
+ else
+ {
+ GetShortPathName(Filename, shortfilepath, MAX_PATH);
+ s = basename(shortfilepath);
+ strcpy(shortfilename, s);
+ if (s != NULL)
+ free(s);
+ if (strcmp(ffb.ff_name, shortfilename))
+ {
+ if (!quiet)
+ printf("Filename \"%s\" will be shortened to %s\n",
+ ffb.ff_name, shortfilename);
+ PressAnyKey();
+ }
+ }
+
+ Len = strlen(shortfilename);
+ if (Len > namelen)
+ {
+ if (!quiet)
+ printf("Filename \"%s\" will be truncated to %d characters.\n",
+ shortfilename, namelen);
+ PressAnyKey();
+ }
+ shortfilename[namelen] = 0;
+ if (namelen == 12)
+ sprintf(FormattedLine, "%-12s %-64.64s", shortfilename, Description);
+ else if (ragged && terminalsized)
+ {
+ if (Len > 14) /* Squeeze every last char out of the 80. */
+ sprintf(FormattedLine, "%s %-64.64s", shortfilename, Description);
+ else
+ sprintf(FormattedLine, "%-14s %-64.64s", shortfilename, Description);
+ }
+ else if (ragged && longDescriptions)
+ {
+ if (Len > 12)
+ sprintf(FormattedLine, "%s %s", shortfilename, Description);
+ else
+ sprintf(FormattedLine, "%-12s %s", shortfilename, Description);
+ }
+ else if (ragged)
+ {
+ if (Len > 12)
+ sprintf(FormattedLine, "%s %-64.64s", shortfilename, Description);
+ else
+ sprintf(FormattedLine, "%-12s %-64.64s", shortfilename, Description);
+ }
+ else if (longDescriptions)
+ sprintf(FormattedLine, "%-25s %s", shortfilename, Description);
+ else
+ sprintf(FormattedLine, "%-25s %s", shortfilename, Description);
+
+ if (terminalsized)
+ {
+ if (namelen == 12)
+ FormattedLine[78] = 0;
+ else
+ FormattedLine[80] = 0;
+ }
+
+ if (verbose)
+ {
+ printf("%d:\t%s\n", nLines, FormattedLine);
+ }
+ if (nLines >= maxLines)
+ {
+ /* Let's have another 1000 pointers */
+ maxLines += numlines;
+ Lines = farrealloc(Lines, maxLines * sizeof(char *));
+ if (!Lines)
+ {
+ printf("Out of memory after %d parts\n", nLines);
+ printf("Memory available at beginning: %ld kBytes\n",
+ (farcoreleftStart + 1023) / 1024);
+ exit(1);
+ }
+ }
+ Lines[nLines++] = FormattedLine;
+ if (nLines % 100 == 0)
+ printf("%d parts so far...\r", nLines);
+ }
+ printf("%d parts found in %s.\n", nLines, Dirname);
+ if (nLines == 0)
+ {
+ printf("No parts found, nothing done.\n");
+ exit(0);
+ }
+
+ printf("Sorting...\n");
+ qsort(Lines, nLines, sizeof(Lines[0]),
+ (SortBy == 'n') ? CmpNumber : CmpDescription);
+
+ if (CheckDuplicateDescriptions)
+ {
+ printf("Checking for duplicate descriptions. \"%s\" unchanged.\n", OutFilename);
+ for (i = 0; i < nLines; i += j)
+ {
+ for (j = 1; i + j < nLines; j++)
+ {
+ if (stricmp(Lines[i] + (namelen+2), Lines[i + j] + (namelen+2)) != 0)
+ break; /* OK to break, lines are sorted */
+ if (j == 1) /* First duplicate */
+ printf("%s\n", Lines[i]);
+ printf("%s\n", Lines[i + j]);
+ }
+ if (j > 1) /* Duplicates found */
+ PressAnyKey();
+ }
+ }
+ else
+ {
+ fp = fopen(OutFilename, "wt");
+ if (!fp)
+ {
+ printf("Cannot open \"%s\" for writing.\n", OutFilename);
+ exit(1);
+ }
+ for (i = 0; i < nLines; i++)
+ fprintf(fp, "%s\n", Lines[i]);
+ FileSize = ftell(fp);
+ fclose(fp);
+ printf("\"%s\" successfully written, %ld kBytes\n", OutFilename,
+ (FileSize + 1023) / 1024);
+ }
+
+ if (numlines > 1000) /* if not Borland DOS compiler then skip the mem msg. */
+ {
+ return (0);
+ }
+
+ farcoreleftEnd = farcoreleft();
+
+ printf("Maximum memory usage: %ld kBytes of %ld kBytes available\n",
+ (farcoreleftStart - farcoreleftEnd + 1023) / 1024,
+ (farcoreleftStart + 1023) / 1024);
+
+ return (0);
+}