1 /*
2  * Distribution functions for the ESP Package Manager (EPM).
3  *
4  * Copyright 1999-2019 by Michael R Sweet
5  * Copyright 1999-2010 by Easy Software Products.
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2, or (at your option)
10  * any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  */
17 
18 /*
19  * Include necessary headers...
20  */
21 
22 #include "epm.h"
23 #include <pwd.h>
24 
25 
26 /*
27  * Some versions of Solaris don't define gethostname()...
28  */
29 
30 #ifdef __sun
31 extern int	gethostname(char *, size_t);
32 #endif /* __sun */
33 
34 
35 /*
36  * Local functions...
37  */
38 
39 static int	compare_files(const file_t *f0, const file_t *f1);
40 static void	expand_name(char *buffer, char *name, size_t bufsize, int warn);
41 static char	*get_file(const char *filename, char *buffer, size_t size);
42 static char	*get_inline(const char *term, FILE *fp, char *buffer,
43 		            size_t size);
44 static char	*get_line(char *buffer, int size, FILE *fp,
45 		          struct utsname *platform, const char *format,
46 			  int *skip);
47 static char	*get_string(char **src, char *dst, size_t dstsize);
48 static int	patmatch(const char *, const char *);
49 static int	sort_subpackages(char **a, char **b);
50 static void	update_architecture(char *buffer, size_t bufsize);
51 
52 
53 /*
54  * Conditional "skip" bits...
55  */
56 
57 #define SKIP_SYSTEM	1		/* Not the right system */
58 #define SKIP_FORMAT	2		/* Not the right format */
59 #define SKIP_ARCH	4		/* Not the right architecture */
60 #define SKIP_IF		8		/* Set if the current #if was not satisfied */
61 #define SKIP_IFACTIVE	16		/* Set if we're in an #if */
62 #define SKIP_IFSAT	32		/* Set if an #if statement has been satisfied */
63 #define SKIP_MASK	15		/* Bits to look at */
64 
65 
66 /*
67  * 'add_command()' - Add a command to the distribution...
68  */
69 
70 void
add_command(dist_t * dist,FILE * fp,int type,const char * command,const char * subpkg,const char * section)71 add_command(dist_t     *dist,		/* I - Distribution */
72             FILE       *fp,		/* I - Distribution file */
73             int        type,		/* I - Command type */
74 	    const char *command,	/* I - Command string */
75 	    const char *subpkg,		/* I - Subpackage */
76             const char *section)	/* I - Literal text section */
77 {
78   command_t	*temp;			/* New command */
79   char		buf[16384];		/* File import buffer */
80 
81 
82   if (!strncmp(command, "<<", 2))
83   {
84     for (command += 2; isspace(*command & 255); command ++);
85 
86     command = get_inline(command, fp, buf, sizeof(buf));
87   }
88   else if (command[0] == '<' && command[1])
89   {
90     for (command ++; isspace(*command & 255); command ++);
91 
92     command = get_file(command, buf, sizeof(buf));
93   }
94 
95   if (!command)
96     return;
97 
98   if (dist->num_commands == 0)
99     temp = malloc(sizeof(command_t));
100   else
101     temp = realloc(dist->commands, (dist->num_commands + 1) * sizeof(command_t));
102 
103   if (!temp)
104   {
105     perror("epm: Out of memory allocating a command");
106     return;
107   }
108 
109   dist->commands   = temp;
110   temp             += dist->num_commands;
111   temp->type       = type;
112   temp->command    = strdup(command);
113   if (!temp->command)
114   {
115     perror("epm: Out of memory duplicating a command string");
116     return;
117   }
118   temp->subpackage = subpkg;
119   if (section && *section)
120   {
121     temp->section = strdup(section);
122     if (!temp->section)
123     {
124       perror("epm: Out of memory duplicating a literal section");
125       free(temp->command);
126       return;
127     }
128   }
129   else
130     temp->section = NULL;
131 
132   dist->num_commands ++;
133 }
134 
135 
136 /*
137  * 'add_depend()' - Add a dependency to the distribution...
138  */
139 
140 void
add_depend(dist_t * dist,int type,const char * line,const char * subpkg)141 add_depend(dist_t     *dist,		/* I - Distribution */
142            int        type,		/* I - Type of dependency */
143 	   const char *line,		/* I - Line from file */
144 	   const char *subpkg)		/* I - Subpackage */
145 {
146   int		i;			/* Looping var */
147   depend_t	*temp;			/* New dependency */
148   char		*ptr;			/* Pointer into string */
149   const char	*lineptr;		/* Temporary pointer into line */
150 
151 
152  /*
153   * Allocate memory for the dependency...
154   */
155 
156   if (dist->num_depends == 0)
157     temp = malloc(sizeof(depend_t));
158   else
159     temp = realloc(dist->depends, (dist->num_depends + 1) * sizeof(depend_t));
160 
161   if (temp == NULL)
162   {
163     perror("epm: Out of memory allocating a dependency");
164     return;
165   }
166 
167   dist->depends = temp;
168   temp          += dist->num_depends;
169   dist->num_depends ++;
170 
171  /*
172   * Initialize the dependency record...
173   */
174 
175   memset(temp, 0, sizeof(depend_t));
176 
177   temp->type       = type;
178   temp->subpackage = subpkg;
179 
180  /*
181   * Get the product name string...
182   */
183 
184   for (ptr = temp->product; *line && !isspace(*line & 255); line ++)
185     if (ptr < (temp->product + sizeof(temp->product) - 1))
186       *ptr++ = *line;
187 
188   while (isspace(*line & 255))
189     line ++;
190 
191  /*
192   * Get the version strings, if any...
193   */
194 
195   for (i = 0; *line && i < 2; i ++)
196   {
197    /*
198     * Handle <version, >version, etc.
199     */
200 
201     if (!isalnum(*line & 255))
202     {
203       if (*line == '<' && i == 0)
204       {
205         strlcpy(temp->version[0], "0.0", sizeof(temp->version[0]));
206 	i ++;
207       }
208 
209       while (!isdigit(*line & 255) && *line)
210         line ++;
211     }
212 
213     if (!*line)
214       break;
215 
216    /*
217     * Grab the version string...
218     */
219 
220     for (ptr = temp->version[i]; *line && !isspace(*line & 255); line ++)
221       if (ptr < (temp->version[i] + sizeof(temp->version[i]) - 1))
222 	*ptr++ = *line;
223 
224     while (isspace(*line & 255))
225       line ++;
226 
227    /*
228     * Get the version number, if any...
229     */
230 
231     for (lineptr = line; isdigit(*lineptr & 255); lineptr ++);
232 
233     if (!*line || (!isspace(*lineptr & 255) && *lineptr))
234     {
235      /*
236       * No version number specified, or the next number is a version
237       * string...
238       */
239 
240       temp->vernumber[i] = get_vernumber(temp->version[i]);
241     }
242     else
243     {
244      /*
245       * Grab the version number directly from the line...
246       */
247 
248       temp->vernumber[i] = atoi(line);
249 
250       for (line = lineptr; isspace(*line & 255); line ++);
251     }
252   }
253 
254  /*
255   * Handle assigning default values based on the number of version numbers...
256   */
257 
258   switch (i)
259   {
260     case 0 :
261         strlcpy(temp->version[0], "0.0", sizeof(temp->version[0]));
262 	/* fall through to set max version number */
263     case 1 :
264         strlcpy(temp->version[1], "999.99.99p99", sizeof(temp->version[1]));
265 	temp->vernumber[1] = INT_MAX;
266 	break;
267   }
268 }
269 
270 
271 /*
272  * 'add_description()' - Add a description to the distribution.
273  */
274 
275 void
add_description(dist_t * dist,FILE * fp,const char * description,const char * subpkg)276 add_description(dist_t     *dist,	/* I - Distribution */
277                 FILE       *fp,		/* I - Source file */
278                 const char *description,/* I - Description string */
279                 const char *subpkg)	/* I - Subpackage name */
280 {
281   description_t	*temp;			/* Temporary description array */
282   char		buf[16384];		/* File import buffer */
283 
284 
285   if (!strncmp(description, "<<", 2))
286   {
287     for (description += 2; isspace(*description & 255); description ++);
288 
289     description = get_inline(description, fp, buf, sizeof(buf));
290   }
291   else if (description[0] == '<' && description[1])
292   {
293     for (description ++; isspace(*description & 255); description ++);
294 
295     description = get_file(description, buf, sizeof(buf));
296   }
297 
298   if (description == NULL)
299     return;
300 
301   if (dist->num_descriptions == 0)
302     temp = malloc(sizeof(description_t));
303   else
304     temp = realloc(dist->descriptions,
305                    (dist->num_descriptions + 1) * sizeof(description_t));
306 
307   if (temp == NULL)
308   {
309     perror("epm: Out of memory adding description");
310     return;
311   }
312 
313   dist->descriptions = temp;
314   temp               += dist->num_descriptions;
315   dist->num_descriptions ++;
316 
317   temp->subpackage = subpkg;
318 
319   if ((temp->description = strdup(description)) == NULL)
320     perror("epm: Out of memory duplicating description");
321 }
322 
323 
324 /*
325  * 'add_file()' - Add a file to the distribution.
326  */
327 
328 file_t *				/* O - New file */
add_file(dist_t * dist,const char * subpkg)329 add_file(dist_t     *dist,		/* I - Distribution */
330          const char *subpkg)		/* I - Subpackage name */
331 {
332   file_t	*file;			/* New file */
333 
334 
335   if (dist->num_files == 0)
336     dist->files = (file_t *)malloc(sizeof(file_t));
337   else
338     dist->files = (file_t *)realloc(dist->files, sizeof(file_t) *
339 					         (dist->num_files + 1));
340 
341   file = dist->files + dist->num_files;
342   dist->num_files ++;
343 
344   file->subpackage = subpkg;
345 
346   return (file);
347 }
348 
349 
350 /*
351  * 'add_subpackage()' - Add a subpackage to the distribution.
352  */
353 
354 char *					/* O - Subpackage pointer */
add_subpackage(dist_t * dist,const char * subpkg)355 add_subpackage(dist_t     *dist,	/* I - Distribution */
356                const char *subpkg)	/* I - Subpackage name */
357 {
358   char	**temp,				/* Temporary array pointer */
359 	*s;				/* Subpackage pointer */
360 
361 
362   if (dist->num_subpackages == 0)
363     temp = malloc(sizeof(char *));
364   else
365     temp = realloc(dist->subpackages,
366                    (dist->num_subpackages + 1) * sizeof(char *));
367 
368   if (temp == NULL)
369     return (NULL);
370 
371   dist->subpackages = temp;
372   temp              += dist->num_subpackages;
373   dist->num_subpackages ++;
374 
375   *temp = s = strdup(subpkg);
376 
377   if (dist->num_subpackages > 1)
378     qsort(dist->subpackages, (size_t)dist->num_subpackages, sizeof(char *),
379           (int (*)(const void *, const void *))sort_subpackages);
380 
381  /*
382   * Return the new string...
383   */
384 
385   return (s);
386 }
387 
388 
389 /*
390  * 'find_subpackage()' - Find a subpackage in the distribution.
391  */
392 
393 char *					/* O - Subpackage pointer */
find_subpackage(dist_t * dist,const char * subpkg)394 find_subpackage(dist_t     *dist,	/* I - Distribution */
395                 const char *subpkg)	/* I - Subpackage name */
396 {
397   char	**match;			/* Matching subpackage */
398 
399 
400   if (!subpkg || !*subpkg)
401     return (NULL);
402 
403   if (dist->num_subpackages > 0)
404     match = bsearch(&subpkg, dist->subpackages, (size_t)dist->num_subpackages,
405                     sizeof(char *),
406                     (int (*)(const void *, const void *))sort_subpackages);
407   else
408     match = NULL;
409 
410   if (match != NULL)
411     return (*match);
412   else
413     return (add_subpackage(dist, subpkg));
414 }
415 
416 
417 /*
418  * 'free_dist()' - Free memory used by a distribution.
419  */
420 
421 void
free_dist(dist_t * dist)422 free_dist(dist_t *dist)			/* I - Distribution to free */
423 {
424   int	i;				/* Looping var */
425 
426 
427   if (dist->num_files > 0)
428     free(dist->files);
429 
430   for (i = 0; i < dist->num_descriptions; i ++)
431     free(dist->descriptions[i].description);
432 
433   if (dist->num_descriptions)
434     free(dist->descriptions);
435 
436   for (i = 0; i < dist->num_subpackages; i ++)
437     free(dist->subpackages[i]);
438 
439   if (dist->num_subpackages)
440     free(dist->subpackages);
441 
442   for (i = 0; i < dist->num_commands; i ++)
443   {
444     free(dist->commands[i].command);
445     if (dist->commands[i].section)
446       free(dist->commands[i].section);
447   }
448 
449   if (dist->num_commands)
450     free(dist->commands);
451 
452   if (dist->num_depends)
453     free(dist->depends);
454 
455   free(dist);
456 }
457 
458 
459 
460 /*
461  * 'getoption()' - Get an option from a file.
462  */
463 
464 const char *				/* O - Value */
get_option(file_t * file,const char * name,const char * defval)465 get_option(file_t     *file,		/* I - File */
466            const char *name,		/* I - Name of option */
467 	   const char *defval)		/* I - Default value of option */
468 {
469   char		*ptr;			/* Pointer to option */
470   static char	option[256];		/* Copy of file option */
471 
472 
473  /*
474   * See if the option exists...
475   */
476 
477   snprintf(option, sizeof(option), "%s(", name);
478 
479   if ((ptr = strstr(file->options, option)) == NULL)
480     return (defval);
481 
482  /*
483   * Yes, copy the value and truncate at the first ")"...
484   */
485 
486   ptr += strlen(option);
487   memmove(option, ptr, strlen(ptr) + 1);
488 
489   if ((ptr = strchr(option, ')')) != NULL)
490   {
491     *ptr = '\0';
492     return (option);
493   }
494   else
495     return (defval);
496 }
497 
498 
499 
500 
501 /*
502  * 'get_platform()' - Get the operating system information...
503  */
504 
505 void
get_platform(struct utsname * platform)506 get_platform(struct utsname *platform)	/* O - Platform info */
507 {
508   char	*temp;				/* Temporary pointer */
509 #ifdef __APPLE__
510   FILE	*fp;				/* SystemVersion.plist file */
511   char	line[1024],			/* Line from file */
512 	*ptr;				/* Pointer into line */
513   int	major, minor;			/* Major and minor release numbers */
514 #endif /* __APPLE__ */
515 
516 
517  /*
518   * Get the system identification information...
519   */
520 
521   uname(platform);
522 
523 #ifdef __APPLE__
524  /*
525   * Try to get the OS version number from the SystemVersion.plist file.
526   * If not present, use the uname() results for Darwin...
527   */
528 
529   if ((fp = fopen("/System/Library/CoreServices/SystemVersion.plist", "r")) != NULL)
530   {
531     ptr = NULL;
532 
533     while (fgets(line, sizeof(line), fp) != NULL)
534       if ((ptr = strstr(line, "ProductUserVisibleVersion")) != NULL)
535         break;
536       else if ((ptr = strstr(line, "ProductVersion")) != NULL)
537         break;
538 
539     if (ptr && fgets(line, sizeof(line), fp) != NULL)
540     {
541       major = 10;
542       minor = 2;
543 
544       if ((ptr = strstr(line, "<string>")) != NULL)
545         sscanf(ptr + 8, "%d.%d", &major, &minor);
546 
547       snprintf(platform->release, sizeof(platform->release), "%d.%d", major, minor);
548     }
549     else
550     {
551      /*
552       * Couldn't find the version number, so assume it is 10.1...
553       */
554 
555       strlcpy(platform->release, "10.1", sizeof(platform->release));
556     }
557 
558     fclose(fp);
559 
560     strlcpy(platform->sysname, "macos", sizeof(platform->sysname));
561   }
562 #endif /* __APPLE__ */
563 
564  /*
565   * Adjust the CPU type accordingly...
566   */
567 
568 #ifdef __sgi
569   strlcpy(platform->machine, "mips", sizeof(platform->machine));
570 #elif defined(__hpux)
571   strlcpy(platform->machine, "hppa", sizeof(platform->machine));
572 #elif defined(_AIX)
573   strlcpy(platform->machine, "ppc", sizeof(platform->machine));
574 #else
575   update_architecture(platform->machine, sizeof(platform->machine));
576 #endif /* __sgi */
577 
578 #ifdef _AIX
579  /*
580   * AIX stores the major and minor version numbers separately;
581   * combine them...
582   */
583 
584   snprintf(platform->release, sizeof(platform->release), "%d.%d", atoi(platform->version), atoi(platform->release));
585 #else
586  /*
587   * Remove any extra junk from the release number - we just want the
588   * major and minor numbers...
589   */
590 
591   while (!isdigit((int)platform->release[0]) && platform->release[0])
592     memmove(platform->release, platform->release + 1, strlen(platform->release));
593 
594   if (platform->release[0] == '.')
595     memmove(platform->release, platform->release + 1, strlen(platform->release));
596 
597   for (temp = platform->release; *temp && isdigit(*temp & 255); temp ++);
598 
599   if (*temp == '.')
600     for (temp ++; *temp && isdigit(*temp & 255); temp ++);
601 
602   *temp = '\0';
603 #endif /* _AIX */
604 
605  /*
606   * Convert the operating system name to lowercase, and strip out
607   * hyphens and underscores...
608   */
609 
610   for (temp = platform->sysname; *temp != '\0'; temp ++)
611     if (*temp == '-' || *temp == '_')
612     {
613       memmove(temp, temp + 1, strlen(temp));
614       temp --;
615     }
616     else
617       *temp = tolower(*temp);
618 
619  /*
620   * SunOS 5.x is really Solaris 2.x or Solaris X, and OSF1 is really
621   * Digital UNIX a.k.a. Compaq Tru64 UNIX...
622   */
623 
624   if (!strcmp(platform->sysname, "sunos") && platform->release[0] >= '5')
625   {
626     strlcpy(platform->sysname, "solaris", sizeof(platform->sysname));
627 
628     if (atoi(platform->release + 2) < 7)
629       platform->release[0] -= 3;
630     else
631     {
632      /*
633       * Strip 5. from the front of the version number...
634       */
635 
636       for (temp = platform->release; temp[2]; temp ++)
637         *temp = temp[2];
638 
639       *temp = '\0';
640     }
641   }
642   else if (!strcmp(platform->sysname, "osf1"))
643     strlcpy(platform->sysname, "tru64", sizeof(platform->sysname)); /* AKA Digital UNIX */
644   else if (!strcmp(platform->sysname, "irix64"))
645     strlcpy(platform->sysname, "irix", sizeof(platform->sysname)); /* IRIX */
646 
647 #ifdef DEBUG
648   printf("sysname = %s\n", platform->sysname);
649   printf("release = %s\n", platform->release);
650   printf("machine = %s\n", platform->machine);
651 #endif /* DEBUG */
652 }
653 
654 
655 /*
656  * 'get_runlevels()' - Get the run levels for the specified init script.
657  */
658 
659 const char *				/* O - Run levels */
get_runlevels(file_t * file,const char * deflevels)660 get_runlevels(file_t     *file,		/* I - File */
661               const char *deflevels)	/* I - Default run levels */
662 {
663   const char	*runlevels;		/* Pointer to runlevels option */
664 
665 
666   if ((runlevels = strstr(file->options, "runlevel(")) != NULL)
667     runlevels += 9;
668   else if ((runlevels = strstr(file->options, "runlevels(")) != NULL)
669     runlevels += 10;			/* Compatible mis-spelling... */
670   else
671     runlevels = deflevels;
672 
673   return (runlevels);
674 }
675 
676 
677 /*
678  * 'get_start()' - Get the start number for an init script.
679  */
680 
681 int					/* O - Start number */
get_start(file_t * file,int defstart)682 get_start(file_t *file,			/* I - File */
683           int    defstart)		/* I - Default start number */
684 {
685   const char	*start;			/* Pointer to start option */
686 
687 
688   if ((start = strstr(file->options, "start(")) != NULL)
689     return (atoi(start + 6));
690   else
691     return (defstart);
692 }
693 
694 
695 /*
696  * 'get_stop()' - Get the stop number for an init script.
697  */
698 
699 int					/* O - Start number */
get_stop(file_t * file,int defstop)700 get_stop(file_t *file,			/* I - File */
701           int    defstop)		/* I - Default stop number */
702 {
703   const char	*stop;			/* Pointer to stop option */
704 
705 
706   if ((stop = strstr(file->options, "stop(")) != NULL)
707     return (atoi(stop + 5));
708   else
709     return (defstop);
710 }
711 
712 
713 /*
714  * 'new_dist()' - Create a new, empty software distribution.
715  */
716 
717 dist_t *				/* O - New distribution */
new_dist(void)718 new_dist(void)
719 {
720  /*
721   * Create a new, blank distribution...
722   */
723 
724   return ((dist_t *)calloc(sizeof(dist_t), 1));
725 }
726 
727 
728 /*
729  * 'read_dist()' - Read a software distribution.
730  */
731 
732 dist_t *				/* O - New distribution */
read_dist(const char * filename,struct utsname * platform,const char * format)733 read_dist(const char     *filename,	/* I - Main distribution list file */
734           struct utsname *platform,	/* I - Platform information */
735           const char     *format)	/* I - Format of distribution */
736 {
737   FILE		*listfiles[10];		/* File lists */
738   int		listlevel;		/* Level in file list */
739   char		line[2048],		/* Expanded line from list file */
740 		buf[1024];		/* Original line from list file */
741   int		type;			/* File type */
742   char		dst[256],		/* Destination path */
743 		src[256],		/* Source path */
744 		pattern[256],		/* Pattern for source files */
745 		user[32],		/* User */
746 		group[32],		/* Group */
747 		*temp,			/* Temporary pointer */
748 		options[256];		/* File options */
749   mode_t	mode;			/* File permissions */
750   int		skip;			/* 1 = skip files, 0 = archive files */
751   dist_t	*dist;			/* Distribution data */
752   file_t	*file;			/* Distribution file */
753   struct stat	fileinfo;		/* File information */
754   DIR		*dir;			/* Directory */
755   DIRENT	*dent;			/* Directory entry */
756   struct passwd	*pwd;			/* Password entry */
757   const char	*subpkg;		/* Subpackage */
758 
759 
760  /*
761   * Create a new, blank distribution...
762   */
763 
764   dist = new_dist();
765 
766  /*
767   * Open the main list file...
768   */
769 
770   if ((listfiles[0] = fopen(filename, "r")) == NULL)
771   {
772     fprintf(stderr, "epm: Unable to open list file \"%s\" -\n     %s\n",
773             filename, strerror(errno));
774     return (NULL);
775   }
776 
777  /*
778   * Find any product descriptions, etc. in the list file...
779   */
780 
781   if (Verbosity)
782     puts("Searching for product information...");
783 
784   skip      = 0;
785   listlevel = 0;
786   subpkg    = NULL;
787 
788   do
789   {
790     while (get_line(buf, sizeof(buf), listfiles[listlevel], platform, format,
791                     &skip) != NULL)
792     {
793      /*
794       * Do variable substitution...
795       */
796 
797       line[0] = buf[0]; /* Don't expand initial $ */
798       expand_name(line + 1, buf + 1, sizeof(line) - 1,
799                   strncmp(buf, "%if", 3) || strncmp(buf, "%elseif", 7));
800 
801      /*
802       * Check line for config stuff...
803       */
804 
805       if (line[0] == '%')
806       {
807        /*
808         * Find whitespace...
809 	*/
810 
811         for (temp = line; !isspace(*temp & 255) && *temp; temp ++);
812 	for (; isspace(*temp & 255); *temp++ = '\0');
813 
814        /*
815         * Process directive...
816         */
817 
818 	if (!strcmp(line, "%include"))
819 	{
820 	  listlevel ++;
821 
822 	  if ((listfiles[listlevel] = fopen(temp, "r")) == NULL)
823 	  {
824 	    fprintf(stderr, "epm: Unable to include \"%s\" -\n     %s\n", temp,
825 	            strerror(errno));
826 	    listlevel --;
827 	  }
828 	}
829 	else if (!strcmp(line, "%description"))
830 	  add_description(dist, listfiles[listlevel], temp, subpkg);
831 	else if (!strcmp(line, "%preinstall"))
832           add_command(dist, listfiles[listlevel], COMMAND_PRE_INSTALL, temp,
833 	              subpkg, NULL);
834 	else if (!strcmp(line, "%install") || !strcmp(line, "%postinstall"))
835           add_command(dist, listfiles[listlevel], COMMAND_POST_INSTALL, temp,
836 	              subpkg, NULL);
837 	else if (!strcmp(line, "%remove") || !strcmp(line, "%preremove"))
838           add_command(dist, listfiles[listlevel], COMMAND_PRE_REMOVE, temp,
839 	              subpkg, NULL);
840 	else if (!strcmp(line, "%postremove"))
841           add_command(dist, listfiles[listlevel], COMMAND_POST_REMOVE, temp,
842 	              subpkg, NULL);
843 	else if (!strcmp(line, "%prepatch"))
844           add_command(dist, listfiles[listlevel], COMMAND_PRE_PATCH, temp,
845 	              subpkg, NULL);
846 	else if (!strcmp(line, "%patch") || !strcmp(line, "%postpatch"))
847           add_command(dist, listfiles[listlevel], COMMAND_POST_PATCH, temp,
848 	              subpkg, NULL);
849 	else if (!strncmp(line, "%literal(", 9))
850         {
851           char	*ptr,			/* Pointer to parenthesis */
852 		*section;		/* Section for literal text */
853 
854 
855 	  section = line + 9;
856 	  if ((ptr = strchr(section, ')')) != NULL)
857 	  {
858 	    *ptr = '\0';
859 
860 	    add_command(dist, listfiles[listlevel], COMMAND_LITERAL, temp,
861 			subpkg, section);
862 	  }
863 	  else
864 	    fputs("epm: Ignoring bad %literal(section) line in list file.\n",
865 		  stderr);
866         }
867         else if (!strcmp(line, "%product"))
868 	{
869           if (!dist->product[0])
870             strlcpy(dist->product, temp, sizeof(dist->product));
871 	  else
872 	    fputs("epm: Ignoring %product line in list file.\n", stderr);
873 	}
874 	else if (!strcmp(line, "%copyright"))
875 	{
876           if (!dist->copyright[0])
877             strlcpy(dist->copyright, temp, sizeof(dist->copyright));
878 	  else
879 	    fputs("epm: Ignoring %copyright line in list file.\n", stderr);
880 	}
881 	else if (!strcmp(line, "%vendor"))
882 	{
883           if (!dist->vendor[0])
884             strlcpy(dist->vendor, temp, sizeof(dist->vendor));
885 	  else
886 	    fputs("epm: Ignoring %vendor line in list file.\n", stderr);
887 	}
888 	else if (!strcmp(line, "%packager"))
889 	{
890           if (!dist->packager[0])
891             strlcpy(dist->packager, temp, sizeof(dist->packager));
892 	  else
893 	    fputs("epm: Ignoring %packager line in list file.\n", stderr);
894 	}
895 	else if (!strcmp(line, "%license"))
896 	{
897           if (!dist->license[0])
898             strlcpy(dist->license, temp, sizeof(dist->license));
899 	  else
900 	    fputs("epm: Ignoring %license line in list file.\n", stderr);
901 	}
902 	else if (!strcmp(line, "%readme"))
903 	{
904           if (!dist->readme[0])
905             strlcpy(dist->readme, temp, sizeof(dist->readme));
906 	  else
907 	    fputs("epm: Ignoring %readme line in list file.\n", stderr);
908 	}
909 	else if (!strcmp(line, "%subpackage"))
910 	{
911 	  subpkg = find_subpackage(dist, temp);
912 	}
913 	else if (!strcmp(line, "%version"))
914 	{
915           if (!dist->version[0])
916 	  {
917 	    if (strchr(temp, ':'))
918 	    {
919 	     /*
920 	      * Grab the epoch...
921 	      */
922 
923 	      dist->epoch = (int)strtol(temp, &temp, 10);
924 
925 	      if (*temp == ':')
926 	        temp ++;
927 	    }
928 
929             strlcpy(dist->version, temp, sizeof(dist->version));
930 	    if ((temp = strchr(dist->version, ' ')) != NULL)
931 	    {
932 	      *temp++ = '\0';
933 	      dist->vernumber = atoi(temp);
934 	    }
935 	    else
936 	      dist->vernumber = get_vernumber(dist->version);
937 
938             if ((temp = strrchr(dist->version, '-')) != NULL)
939 	    {
940 	      *temp++ = '\0';
941 	      strlcpy(dist->release, temp, sizeof(dist->release));
942 	    }
943 	  }
944 	}
945 	else if (!strcmp(line, "%release"))
946 	{
947 	  strlcpy(dist->release, temp, sizeof(dist->release));
948 	  dist->vernumber += atoi(temp);
949 	}
950 	else if (!strcmp(line, "%incompat"))
951 	  add_depend(dist, DEPEND_INCOMPAT, temp, subpkg);
952 	else if (!strcmp(line, "%provides"))
953 	  add_depend(dist, DEPEND_PROVIDES, temp, subpkg);
954 	else if (!strcmp(line, "%replaces"))
955 	  add_depend(dist, DEPEND_REPLACES, temp, subpkg);
956 	else if (!strcmp(line, "%requires"))
957 	  add_depend(dist, DEPEND_REQUIRES, temp, subpkg);
958 	else
959 	{
960 	  fprintf(stderr, "epm: Unknown directive \"%s\" ignored.\n", line);
961 	  fprintf(stderr, "     %s %s\n", line, temp);
962 	}
963       }
964       else if (line[0] == '$')
965       {
966        /*
967         * Define a variable...
968 	*/
969 
970         if (line[1] == '{' && (temp = strchr(line + 2, '}')) != NULL)
971 	{
972 	 /*
973 	  * Remove {} from name...
974 	  */
975 
976 	  memmove(temp, temp + 1, strlen(temp));
977           memmove(line + 1, line + 2, strlen(line + 1));
978         }
979 	else if (line[1] == '(' && (temp = strchr(line + 2, ')')) != NULL)
980 	{
981 	 /*
982 	  * Remove () from name...
983 	  */
984 
985 	  memmove(temp, temp + 1, strlen(temp));
986           memmove(line + 1, line + 2, strlen(line + 1));
987         }
988 
989         if ((temp = strchr(line + 1, '=')) != NULL)
990 	{
991 	 /*
992 	  * Only define the variable if it is not in the environment
993 	  * or on the command-line.
994 	  */
995 
996 	  *temp = '\0';
997 
998 	  if (getenv(line + 1) == NULL)
999 	  {
1000 	    *temp = '=';
1001             if ((temp = strdup(line + 1)) != NULL)
1002 	      putenv(temp);
1003 	  }
1004 	}
1005       }
1006       else
1007       {
1008         type = line[0];
1009 	if (!isspace(line[1] & 255))
1010 	{
1011 	  fprintf(stderr, "epm: Expected whitespace after file type: %s\n", line);
1012 	  continue;
1013 	}
1014 
1015         temp = line + 2;
1016 	mode = strtol(temp, &temp, 8);
1017 	if (temp == (line + 2))
1018 	{
1019 	  fprintf(stderr, "epm: Expected file permissions after file type: %s\n", line);
1020 	  continue;
1021 	}
1022 
1023         if (get_string(&temp, user, sizeof(user)) == NULL)
1024 	{
1025 	  fprintf(stderr, "epm: Expected user after file permissions: %s\n", line);
1026 	  continue;
1027 	}
1028 
1029         if (get_string(&temp, group, sizeof(group)) == NULL)
1030 	{
1031 	  fprintf(stderr, "epm: Expected group after user: %s\n", line);
1032 	  continue;
1033 	}
1034 
1035         if (get_string(&temp, dst, sizeof(dst)) == NULL)
1036 	{
1037 	  fprintf(stderr, "epm: Expected destination after group: %s\n", line);
1038 	  continue;
1039 	}
1040 
1041         get_string(&temp, src, sizeof(src));
1042 
1043         get_string(&temp, options, sizeof(options));
1044 
1045 	if (tolower(type) == 'd' || type == 'R')
1046 	{
1047 	  strlcpy(options, src, sizeof(options));
1048 	  src[0] = '\0';
1049 	}
1050 
1051 #ifdef __osf__ /* Remap group "sys" to "system" */
1052         if (!strcmp(group, "sys"))
1053 	  strlcpy(group, "system", sizeof(group));
1054 #elif defined(__linux) /* Remap group "sys" to "root" */
1055         if (!strcmp(group, "sys"))
1056 	  strlcpy(group, "root", sizeof(group));
1057 #endif /* __osf__ */
1058 
1059         if ((temp = strrchr(src, '/')) == NULL)
1060 	  temp = src;
1061 	else
1062 	  temp ++;
1063 
1064         for (; *temp; temp ++)
1065 	  if (strchr("*?[", *temp))
1066 	    break;
1067 
1068         if (*temp)
1069 	{
1070 	 /*
1071 	  * Add using wildcards...
1072 	  */
1073 
1074           if ((temp = strrchr(src, '/')) == NULL)
1075 	    temp = src;
1076 	  else
1077 	    *temp++ = '\0';
1078 
1079 	  strlcpy(pattern, temp, sizeof(pattern));
1080 
1081           if (dst[strlen(dst) - 1] != '/')
1082 	    strlcat(dst, "/", sizeof(dst));
1083 
1084           if (temp == src)
1085 	    dir = opendir(".");
1086 	  else
1087 	    dir = opendir(src);
1088 
1089           if (dir == NULL)
1090 	    fprintf(stderr, "epm: Unable to open directory \"%s\": %s\n", src, strerror(errno));
1091           else
1092 	  {
1093 	   /*
1094 	    * Make sure we have a directory separator...
1095 	    */
1096 
1097 	    if (temp > src)
1098 	      temp[-1] = '/';
1099 
1100 	    while ((dent = readdir(dir)) != NULL)
1101 	    {
1102 	      strlcpy(temp, dent->d_name, sizeof(src) - (size_t)(temp - src));
1103 	      if (stat(src, &fileinfo))
1104 	        continue; /* Skip files we can't read */
1105 
1106               if (S_ISDIR(fileinfo.st_mode))
1107 	        continue; /* Skip directories */
1108 
1109               if (!patmatch(dent->d_name, pattern))
1110 	        continue;
1111 
1112               file = add_file(dist, subpkg);
1113 
1114               file->type = type;
1115 	      file->mode = mode;
1116               strlcpy(file->src, src, sizeof(file->src));
1117 	      strlcpy(file->dst, dst, sizeof(file->dst));
1118 	      strlcat(file->dst, dent->d_name, sizeof(file->dst));
1119 	      strlcpy(file->user, user, sizeof(file->user));
1120 	      strlcpy(file->group, group, sizeof(file->group));
1121 	      strlcpy(file->options, options, sizeof(file->options));
1122 	    }
1123 
1124             closedir(dir);
1125 	  }
1126 	}
1127 	else
1128 	{
1129          /*
1130 	  * Add single file...
1131 	  */
1132 
1133           file = add_file(dist, subpkg);
1134 
1135           file->type = type;
1136 	  file->mode = mode;
1137           strlcpy(file->src, src, sizeof(file->src));
1138 	  strlcpy(file->dst, dst, sizeof(file->dst));
1139 	  strlcpy(file->user, user, sizeof(file->user));
1140 	  strlcpy(file->group, group, sizeof(file->group));
1141 	  strlcpy(file->options, options, sizeof(file->options));
1142 	}
1143       }
1144     }
1145 
1146     fclose(listfiles[listlevel]);
1147     listlevel --;
1148   }
1149   while (listlevel >= 0);
1150 
1151   if (!dist->packager[0])
1152   {
1153    /*
1154     * Assign a default packager name...
1155     */
1156 
1157     gethostname(buf, sizeof(buf));
1158 
1159     setpwent();
1160     if ((pwd = getpwuid(getuid())) != NULL)
1161       snprintf(dist->packager, sizeof(dist->packager), "%s@%s", pwd->pw_name,
1162                buf);
1163     else
1164       snprintf(dist->packager, sizeof(dist->packager), "unknown@%s", buf);
1165   }
1166 
1167   sort_dist_files(dist);
1168 
1169   return (dist);
1170 }
1171 
1172 
1173 /*
1174  * 'sort_dist_files()' - Sort the files in the distribution.
1175  */
1176 
1177 void
sort_dist_files(dist_t * dist)1178 sort_dist_files(dist_t *dist)		/* I - Distribution to sort */
1179 {
1180   int		i;			/* Looping var */
1181   file_t	*file;			/* File in distribution */
1182 
1183 
1184  /*
1185   * Sort the files...
1186   */
1187 
1188   if (dist->num_files > 1)
1189     qsort(dist->files, (size_t)dist->num_files, sizeof(file_t),
1190           (int (*)(const void *, const void *))compare_files);
1191 
1192  /*
1193   * Remove duplicates...
1194   */
1195 
1196   for (i = dist->num_files - 1, file = dist->files; i > 0; i --, file ++)
1197     if (!strcmp(file[0].dst, file[1].dst))
1198     {
1199       if (file[0].type == file[1].type && file[0].mode == file[1].mode &&
1200           !strcmp(file[0].src, file[1].src) &&
1201           !strcmp(file[0].user, file[1].user) &&
1202 	  !strcmp(file[0].group, file[1].group) &&
1203 	  !strcmp(file[0].options, file[1].options))
1204       {
1205        /*
1206         * Ignore exact duplicates...
1207 	*/
1208 
1209 	memcpy(file, file + 1, i * sizeof(file_t));
1210 	dist->num_files --;
1211 	file --;
1212       }
1213       else
1214         fprintf(stderr, "epm: Duplicate destination path \"%s\" with different info:\n"
1215 	                "     \"%c %04o %s %s\" from source \"%s\"\n"
1216 			"     \"%c %04o %s %s\" from source \"%s\"\n",
1217 	        file[0].dst,
1218 		file[0].type, file[0].mode, file[0].user, file[0].group, file[0].src,
1219 		file[1].type, file[1].mode, file[1].user, file[1].group, file[1].src);
1220     }
1221 }
1222 
1223 
1224 /*
1225  * 'write_dist()' - Write a distribution list file...
1226  */
1227 
1228 int					/* O - 0 on success, -1 on failure */
write_dist(const char * listname,dist_t * dist)1229 write_dist(const char *listname,	/* I - File to write to */
1230            dist_t     *dist)		/* I - Distribution to write */
1231 {
1232   int		i;			/* Looping var */
1233   int		is_inline;		/* Inline text? */
1234   char		listbck[1024],		/* Backup filename */
1235 		*ptr;			/* Pointer into command string */
1236   FILE		*listfile;		/* Output file */
1237   file_t	*file;			/* Current file entry */
1238   time_t	curtime;		/* Current time */
1239   struct tm	curdate;		/* Current date */
1240   char		curstring[256];		/* Current date/time string */
1241   const char	*subpkg;		/* Current subpackage */
1242   static const char *commands[] =	/* Command strings */
1243 		{
1244 		  "%preinstall",
1245 		  "%postinstall",
1246 		  "%prepatch",
1247 		  "%postpatch",
1248 		  "%preremove",
1249 		  "%postremove"
1250 		},
1251 		*depends[] =		/* Dependency strings */
1252 		{
1253 		  "%requires",
1254 		  "%incompat",
1255 		  "%replaces",
1256 		  "%provides"
1257 		};
1258 
1259 
1260  /*
1261   * Make a backup of the list file...
1262   */
1263 
1264   snprintf(listbck, sizeof(listbck), "%s.O", listname);
1265 
1266   rename(listname, listbck);
1267 
1268  /*
1269   * Open the list file...
1270   */
1271 
1272   if ((listfile = fopen(listname, "w")) == NULL)
1273   {
1274     rename(listbck, listname);
1275     return (-1);
1276   }
1277 
1278  /*
1279   * Write the list file...
1280   */
1281 
1282   time(&curtime);
1283   localtime_r(&curtime, &curdate);
1284 
1285   strftime(curstring, sizeof(curstring), "# List file created on %c by "
1286            EPM_VERSION "\n", &curdate);
1287   fputs(curstring, listfile);
1288 
1289   if (dist->product[0])
1290     fprintf(listfile, "%%product %s\n", dist->product);
1291   if (dist->version[0])
1292     fprintf(listfile, "%%version %s %d\n", dist->version, dist->vernumber);
1293   if (dist->release[0])
1294     fprintf(listfile, "%%release %s\n", dist->release);
1295   if (dist->copyright[0])
1296     fprintf(listfile, "%%copyright %s\n", dist->copyright);
1297   if (dist->vendor[0])
1298     fprintf(listfile, "%%vendor %s\n", dist->vendor);
1299   if (dist->packager[0])
1300     fprintf(listfile, "%%packager %s\n", dist->packager);
1301   if (dist->license[0])
1302     fprintf(listfile, "%%license %s\n", dist->license);
1303   if (dist->readme[0])
1304     fprintf(listfile, "%%readme %s\n", dist->readme);
1305 
1306   subpkg = NULL;
1307   for (i = 0; i < dist->num_descriptions; i ++)
1308   {
1309     if (dist->descriptions[i].subpackage != subpkg)
1310     {
1311       subpkg = dist->descriptions[i].subpackage;
1312       fprintf(listfile, "%%subpackage %s\n", subpkg ? subpkg : "");
1313     }
1314 
1315     if (strchr(dist->descriptions[i].description, '\n') != NULL)
1316       fprintf(listfile, "%%description <<EPM-END-INLINE\n%s\nEPM-END-INLINE\n",
1317               dist->descriptions[i].description);
1318     else
1319       fprintf(listfile, "%%description %s\n",
1320               dist->descriptions[i].description);
1321   }
1322 
1323   for (i = 0; i < dist->num_depends; i ++)
1324   {
1325     if (dist->depends[i].subpackage != subpkg)
1326     {
1327       subpkg = dist->depends[i].subpackage;
1328       fprintf(listfile, "%%subpackage %s\n", subpkg ? subpkg : "");
1329     }
1330 
1331     fprintf(listfile, "%s %s", depends[(int)dist->depends[i].type],
1332             dist->depends[i].product);
1333 
1334     if (dist->depends[i].version[0][0] ||
1335         dist->depends[i].version[1][0])
1336     {
1337       fprintf(listfile, " %s %d %s %d\n",
1338               dist->depends[i].version[0],
1339 	      dist->depends[i].vernumber[0],
1340               dist->depends[i].version[1],
1341 	      dist->depends[i].vernumber[1]);
1342     }
1343     else
1344       putc('\n', listfile);
1345   }
1346 
1347   for (i = 0; i < dist->num_commands; i ++)
1348   {
1349     if (dist->commands[i].subpackage != subpkg)
1350     {
1351       subpkg = dist->commands[i].subpackage;
1352       fprintf(listfile, "%%subpackage %s\n", subpkg ? subpkg : "");
1353     }
1354 
1355     fputs(commands[(int)dist->commands[i].type], listfile);
1356 
1357     is_inline = strchr(dist->commands[i].command, '\n') != NULL;
1358 
1359     if (is_inline)
1360       fputs(" <<EPM-END-INLINE\n", listfile);
1361     else
1362       putc(' ', listfile);
1363 
1364     for (ptr = dist->commands[i].command; *ptr; ptr ++)
1365     {
1366       if (*ptr == '$' && !is_inline)
1367         putc(*ptr, listfile);
1368 
1369       putc(*ptr, listfile);
1370     }
1371     putc('\n', listfile);
1372 
1373     if (is_inline)
1374       fputs("EPM-END-INLINE\n", listfile);
1375   }
1376 
1377   for (i = dist->num_files, file = dist->files; i > 0; i --, file ++)
1378   {
1379     if (file->subpackage != subpkg)
1380     {
1381       subpkg = file->subpackage;
1382       fprintf(listfile, "%%subpackage %s\n", subpkg ? subpkg : "");
1383     }
1384 
1385     fprintf(listfile, "%c %04o %s %s %s %s",
1386 	    file->type, file->mode, file->user, file->group,
1387 	    file->dst, file->src);
1388 
1389     if (file->options[0])
1390       fprintf(listfile, "%s\n", file->options);
1391     else
1392       putc('\n', listfile);
1393   }
1394 
1395   return (fclose(listfile));
1396 }
1397 
1398 
1399 /*
1400  * 'compare_files()' - Compare the destination filenames.
1401  */
1402 
1403 static int				/* O - Result of comparison */
compare_files(const file_t * f0,const file_t * f1)1404 compare_files(const file_t *f0,		/* I - First file */
1405               const file_t *f1)		/* I - Second file */
1406 {
1407   return (strcmp(f0->dst, f1->dst));
1408 }
1409 
1410 
1411 /*
1412  * 'expand_name()' - Expand a filename with environment variables.
1413  */
1414 
1415 static void
expand_name(char * buffer,char * name,size_t bufsize,int warn)1416 expand_name(char   *buffer,		/* O - Output string */
1417             char   *name,		/* I - Input string */
1418 	    size_t bufsize,		/* I - Size of output string */
1419 	    int    warn)		/* I - Warn when not set? */
1420 {
1421   char	var[255],			/* Environment variable name */
1422 	*varptr,			/* Current position in name */
1423 	delim;				/* Delimiter character */
1424 
1425 
1426   bufsize --;
1427   while (*name != '\0' && bufsize > 0)
1428   {
1429     if (*name == '$')
1430     {
1431       name ++;
1432       if (*name == '$')
1433       {
1434        /*
1435         * Insert a lone $...
1436 	*/
1437 
1438 	*buffer++ = *name++;
1439 	bufsize --;
1440 	continue;
1441       }
1442       else if (*name == '{' || *name == '(')
1443       {
1444        /*
1445         * Bracketed variable name...
1446 	*/
1447 
1448         delim = *name == '{' ? '}' : ')';
1449 
1450 	for (varptr = var, name ++; *name != delim && *name; name ++)
1451           if (varptr < (var + sizeof(var) - 1))
1452 	    *varptr++ = *name;
1453 
1454         if (*name == delim)
1455 	  name ++;
1456       }
1457       else
1458       {
1459        /*
1460         * Unbracketed variable name...
1461 	*/
1462 
1463 	for (varptr = var; !strchr("/ \t\r\n-", *name) && *name; name ++)
1464           if (varptr < (var + sizeof(var) - 1))
1465 	    *varptr++ = *name;
1466       }
1467 
1468       *varptr = '\0';
1469 
1470       if ((varptr = getenv(var)) != NULL)
1471       {
1472 	strlcpy(buffer, varptr, bufsize + 1);
1473         bufsize -= strlen(buffer);
1474 	buffer  += strlen(buffer);
1475       }
1476       else if (warn)
1477         fprintf(stderr, "epm: Variable \"%s\" undefined.\n", var);
1478     }
1479     else
1480     {
1481       *buffer++ = *name++;
1482       bufsize --;
1483     }
1484   }
1485 
1486   *buffer = '\0';
1487 }
1488 
1489 
1490 /*
1491  * 'get_file()' - Read a file into a string...
1492  */
1493 
1494 static char *				/* O  - Pointer to string or NULL on EOF */
get_file(const char * filename,char * buffer,size_t size)1495 get_file(const char *filename,		/* I  - File to read from */
1496          char       *buffer,		/* IO - String buffer */
1497 	 size_t     size)		/* I  - Size of string buffer */
1498 {
1499   FILE		*fp;			/* File buffer */
1500   struct stat	info;			/* File information */
1501   char		*expand;		/* Expansion buffer */
1502 
1503 
1504   if (stat(filename, &info))
1505   {
1506     fprintf(stderr, "epm: Unable to stat \"%s\": %s\n", filename, strerror(errno));
1507     return (NULL);
1508   }
1509 
1510   if (info.st_size > (size - 1))
1511   {
1512     fprintf(stderr,
1513             "epm: File \"%s\" is too large (%d bytes) for buffer (%d bytes)\n",
1514             filename, (int)info.st_size, (int)size - 1);
1515     return (NULL);
1516   }
1517 
1518   if ((fp = fopen(filename, "r")) == NULL)
1519   {
1520     fprintf(stderr, "epm: Unable to open \"%s\": %s\n", filename, strerror(errno));
1521     return (NULL);
1522   }
1523 
1524   if ((fread(buffer, 1, (size_t)info.st_size, fp)) < info.st_size)
1525   {
1526     fprintf(stderr, "epm: Unable to read \"%s\": %s\n", filename, strerror(errno));
1527     fclose(fp);
1528     return (NULL);
1529   }
1530 
1531   fclose(fp);
1532 
1533   if (buffer[info.st_size - 1] == '\n')
1534     buffer[info.st_size - 1] = '\0';
1535   else
1536     buffer[info.st_size] = '\0';
1537 
1538   if (strchr(buffer, '$') != NULL)
1539   {
1540    /*
1541     * Do variable expansion before returning...
1542     */
1543 
1544     expand = strdup(buffer);
1545 
1546     expand_name(buffer, expand, size, 1);
1547 
1548     free(expand);
1549   }
1550 
1551   return (buffer);
1552 }
1553 
1554 
1555 /*
1556  * 'get_inline()' - Read inline lines into a string...
1557  */
1558 
1559 static char *				/* O  - Pointer to string or NULL on EOF */
get_inline(const char * term,FILE * fp,char * buffer,size_t size)1560 get_inline(const char *term,		/* I  - Termination string */
1561            FILE       *fp,		/* I  - File to read from */
1562            char       *buffer,		/* IO - String buffer */
1563 	   size_t     size)		/* I  - Size of string buffer */
1564 {
1565   char		*bufptr;		/* Pointer into buffer */
1566   size_t	left;			/* Remaining bytes in buffer */
1567   size_t	termlen;		/* Length of termination string */
1568   size_t	linelen;		/* Length of line */
1569   char		*expand;		/* Expansion buffer */
1570 
1571 
1572   bufptr  = buffer;
1573   left    = size;
1574   termlen = strlen(term);
1575 
1576   if (termlen == 0)
1577     return (NULL);
1578 
1579   while (fgets(bufptr, (int)left, fp) != NULL)
1580   {
1581     if (!strncmp(bufptr, term, (size_t)termlen) && bufptr[termlen] == '\n')
1582     {
1583       *bufptr = '\0';
1584       break;
1585     }
1586 
1587     linelen = strlen(bufptr);
1588     left    -= linelen;
1589     bufptr  += linelen;
1590 
1591     if (left < 2)
1592     {
1593       fputs("epm: Inline script too long.\n", stderr);
1594       break;
1595     }
1596   }
1597 
1598   if (bufptr > buffer)
1599   {
1600     bufptr --;
1601     if (*bufptr == '\n')
1602       *bufptr = '\0';
1603 
1604     if (strchr(buffer, '$') != NULL)
1605     {
1606      /*
1607       * Do variable expansion before returning...
1608       */
1609 
1610       expand = strdup(buffer);
1611 
1612       expand_name(buffer, expand, size, 1);
1613 
1614       free(expand);
1615     }
1616 
1617     return (buffer);
1618   }
1619   else
1620     return (NULL);
1621 }
1622 
1623 
1624 /*
1625  * 'get_line()' - Get a line from a file, filtering for uname lines...
1626  */
1627 
1628 static char *				/* O - String read or NULL at EOF */
get_line(char * buffer,int size,FILE * fp,struct utsname * platform,const char * format,int * skip)1629 get_line(char           *buffer,	/* I - Buffer to read into */
1630          int            size,		/* I - Size of buffer */
1631 	 FILE           *fp,		/* I - File to read from */
1632 	 struct utsname *platform,	/* I - Platform information */
1633          const char     *format,	/* I - Distribution format */
1634 	 int            *skip)		/* IO - Skip lines? */
1635 {
1636   int		op,			/* Operation (0 = OR, 1 = AND) */
1637 		match;			/* 1 = match, 0 = not */
1638   size_t	namelen,		/* Length of system name + version */
1639 		len;			/* Length of string */
1640   char		*ptr,			/* Pointer into value */
1641 		*bufptr,		/* Pointer into buffer */
1642 		namever[255],		/* Name + version */
1643 		value[255];		/* Value string */
1644   const char	*var;			/* Variable value */
1645 
1646 
1647   while (fgets(buffer, size, fp) != NULL)
1648   {
1649    /*
1650     * Skip comment and blank lines...
1651     */
1652 
1653     if (buffer[0] == '#' || buffer[0] == '\n')
1654       continue;
1655 
1656    /*
1657     * See if this is a %system, %format, or conditional line...
1658     */
1659 
1660     if (!strncmp(buffer, "%system ", 8))
1661     {
1662      /*
1663       * Yes, do filtering based on the OS (+version)...
1664       */
1665 
1666       *skip &= ~SKIP_SYSTEM;
1667 
1668       if (strcmp(buffer + 8, "all\n"))
1669       {
1670 	namelen = strlen(platform->sysname);
1671         bufptr  = buffer + 8;
1672 	snprintf(namever, sizeof(namever), "%s-%s", platform->sysname,
1673 	         platform->release);
1674 
1675 	while (isspace(*bufptr & 255))
1676 	  bufptr ++;
1677 
1678         if (*bufptr != '!')
1679 	  *skip |= SKIP_SYSTEM;
1680 
1681         while (*bufptr)
1682 	{
1683 	 /*
1684           * Skip leading whitespace...
1685 	  */
1686 
1687 	  while (isspace(*bufptr & 255))
1688 	    bufptr ++;
1689 
1690           if (!*bufptr)
1691 	    break;
1692 
1693           if (*bufptr == '!')
1694 	  {
1695 	    op = 1;
1696 	    bufptr ++;
1697 	  }
1698 	  else
1699 	    op = 0;
1700 
1701 	  for (ptr = value;
1702 	       *bufptr && !isspace(*bufptr & 255) &&
1703 	           ptr < (value + sizeof(value) - 1);
1704 	       *ptr++ = *bufptr++);
1705 
1706 	  *ptr = '\0';
1707 
1708           if (!strncmp(value, "dunix", 5))
1709 	    memcpy(value, "tru64", 5); /* Keep existing nul/version */
1710           else if ((!strncmp(value, "darwin", 6) || !strncmp(value, "macosx", 6)) &&
1711 	           !strcmp(platform->sysname, "macos"))
1712           {
1713            /*
1714             * Convert "darwin*" and "macosx*" to "macos*"
1715             */
1716 
1717 	    memcpy(value, "macos", 5);
1718 	    memmove(value + 5, value + 6, strlen(value + 6) + 1);
1719 	  }
1720 
1721           if ((ptr = strchr(value, '-')) != NULL)
1722 	    len = ptr - value;
1723 	  else
1724 	    len = strlen(value);
1725 
1726           if (len < namelen)
1727 	    match = 0;
1728 	  else
1729 	    match = !strncasecmp(value, namever, strlen(value)) ?
1730 	                SKIP_SYSTEM : 0;
1731 
1732           if (op)
1733 	    *skip |= match;
1734 	  else
1735 	    *skip &= ~match;
1736         }
1737       }
1738     }
1739     else if (!strncmp(buffer, "%format ", 8))
1740     {
1741      /*
1742       * Yes, do filtering based on the distribution format...
1743       */
1744 
1745       *skip &= ~SKIP_FORMAT;
1746 
1747       if (strcmp(buffer + 8, "all\n"))
1748       {
1749         bufptr = buffer + 8;
1750 
1751 	while (isspace(*bufptr & 255))
1752 	  bufptr ++;
1753 
1754         if (*bufptr != '!')
1755 	  *skip  |= SKIP_FORMAT;
1756 
1757         while (*bufptr)
1758 	{
1759 	 /*
1760           * Skip leading whitespace...
1761 	  */
1762 
1763 	  while (isspace(*bufptr & 255))
1764 	    bufptr ++;
1765 
1766           if (!*bufptr)
1767 	    break;
1768 
1769           if (*bufptr == '!')
1770 	  {
1771 	    op = 1;
1772 	    bufptr ++;
1773 	  }
1774 	  else
1775 	    op = 0;
1776 
1777 	  for (ptr = value;
1778 	       *bufptr && !isspace(*bufptr & 255) &&
1779 	           ptr < (value + sizeof(value) - 1);
1780 	       *ptr++ = *bufptr++);
1781 
1782 	  *ptr = '\0';
1783 
1784           if (!strcasecmp(value, "osx"))
1785             strlcpy(value, "macos", sizeof(value));
1786 
1787 	  match = !strcasecmp(value, format) ? SKIP_FORMAT : 0;
1788 
1789           if (op)
1790 	    *skip |= match;
1791 	  else
1792 	    *skip &= ~match;
1793         }
1794       }
1795     }
1796     else if (!strncmp(buffer, "%arch ", 6))
1797     {
1798      /*
1799       * Yes, do filtering based on the current architecture...
1800       */
1801 
1802       *skip &= ~SKIP_ARCH;
1803 
1804       if (strcmp(buffer + 6, "all\n"))
1805       {
1806         bufptr = buffer + 8;
1807 
1808 	while (isspace(*bufptr & 255))
1809 	  bufptr ++;
1810 
1811         if (*bufptr != '!')
1812 	  *skip |= SKIP_ARCH;
1813 
1814         while (*bufptr)
1815 	{
1816 	 /*
1817           * Skip leading whitespace...
1818 	  */
1819 
1820 	  while (isspace(*bufptr & 255))
1821 	    bufptr ++;
1822 
1823           if (!*bufptr)
1824 	    break;
1825 
1826           if (*bufptr == '!')
1827 	  {
1828 	    op = 1;
1829 	    bufptr ++;
1830 	  }
1831 	  else
1832 	    op = 0;
1833 
1834 	  for (ptr = value;
1835 	       *bufptr && !isspace(*bufptr & 255) &&
1836 	           ptr < (value + sizeof(value) - 1);
1837 	       *ptr++ = *bufptr++);
1838 
1839 	  *ptr = '\0';
1840 
1841           update_architecture(value, sizeof(value));
1842 
1843 	  match = !strcasecmp(value, platform->machine) ? SKIP_ARCH : 0;
1844 
1845           if (op)
1846 	    *skip |= match;
1847 	  else
1848 	    *skip &= ~match;
1849         }
1850       }
1851     }
1852     else if (!strncmp(buffer, "%ifdef ", 7) ||
1853              !strncmp(buffer, "%elseifdef ", 11))
1854     {
1855      /*
1856       * Yes, do filtering based on the presence of variables...
1857       */
1858 
1859       if ((*skip & SKIP_IFACTIVE) && buffer[1] != 'e')
1860       {
1861        /*
1862         * Nested %if...
1863 	*/
1864 
1865         fputs("epm: Warning, nested %ifdef's are not supported.\n", stderr);
1866 	continue;
1867       }
1868 
1869       if (buffer[1] == 'e')
1870 	bufptr = buffer + 11;
1871       else
1872 	bufptr = buffer + 7;
1873 
1874       *skip |= SKIP_IF | SKIP_IFACTIVE;
1875 
1876       if (*skip & SKIP_IFSAT)
1877         continue;
1878 
1879       while (isspace(*bufptr & 255))
1880 	bufptr ++;
1881 
1882       if (*bufptr == '!')
1883         *skip &= ~SKIP_IF;
1884 
1885       while (*bufptr)
1886       {
1887        /*
1888         * Skip leading whitespace...
1889 	*/
1890 
1891 	while (isspace(*bufptr & 255))
1892 	  bufptr ++;
1893 
1894         if (!*bufptr)
1895 	  break;
1896 
1897        /*
1898         * Get the variable name...
1899 	*/
1900 
1901         if (*bufptr == '!')
1902 	{
1903 	  op = 1;
1904 	  bufptr ++;
1905 	}
1906 	else
1907 	  op = 0;
1908 
1909 	for (ptr = value;
1910 	     *bufptr && !isspace(*bufptr & 255) &&
1911 	         ptr < (value + sizeof(value) - 1);
1912 	     *ptr++ = *bufptr++);
1913 
1914 	*ptr = '\0';
1915 
1916 	match = (getenv(value) != NULL) ? SKIP_IF : 0;
1917 
1918         if (op)
1919 	  *skip |= match;
1920 	else
1921 	  *skip &= ~match;
1922 
1923         if (match)
1924 	  *skip |= SKIP_IFSAT;
1925       }
1926     }
1927     else if (!strncmp(buffer, "%if ", 4) || !strncmp(buffer, "%elseif ", 8))
1928     {
1929      /*
1930       * Yes, do filtering based on the value of variables...
1931       */
1932 
1933       if ((*skip & SKIP_IFACTIVE) && buffer[1] != 'e')
1934       {
1935        /*
1936         * Nested %if...
1937 	*/
1938 
1939         fputs("epm: Warning, nested %if's are not supported.\n", stderr);
1940 	continue;
1941       }
1942 
1943       if (buffer[1] == 'e')
1944         bufptr = buffer + 8;
1945       else
1946 	bufptr = buffer + 4;
1947 
1948       *skip |= SKIP_IF | SKIP_IFACTIVE;
1949 
1950       if (*skip & SKIP_IFSAT)
1951         continue;
1952 
1953       while (isspace(*bufptr & 255))
1954 	bufptr ++;
1955 
1956       if (*bufptr == '!')
1957         *skip &= ~SKIP_IF;
1958 
1959       while (*bufptr)
1960       {
1961        /*
1962         * Skip leading whitespace...
1963 	*/
1964 
1965 	while (isspace(*bufptr & 255))
1966 	  bufptr ++;
1967 
1968         if (!*bufptr)
1969 	  break;
1970 
1971        /*
1972         * Get the variable name...
1973 	*/
1974 
1975         if (*bufptr == '!')
1976 	{
1977 	  op = 1;
1978 	  bufptr ++;
1979 	}
1980 	else
1981 	  op = 0;
1982 
1983 	for (ptr = value;
1984 	     *bufptr && !isspace(*bufptr & 255) &&
1985 	         ptr < (value + sizeof(value) - 1);
1986 	     *ptr++ = *bufptr++);
1987 
1988 	*ptr = '\0';
1989 
1990         match = ((var = getenv(value)) != NULL && *var) ? SKIP_IF : 0;
1991 
1992         if (op)
1993 	  *skip |= match;
1994 	else
1995 	  *skip &= ~match;
1996 
1997         if (match)
1998 	  *skip |= SKIP_IFSAT;
1999       }
2000     }
2001     else if (!strcmp(buffer, "%else\n"))
2002     {
2003      /*
2004       * Handle "else" condition of %ifdef statement...
2005       */
2006 
2007       if (!(*skip & SKIP_IFACTIVE))
2008       {
2009        /*
2010         * No matching %if/%ifdef statement...
2011 	*/
2012 
2013         fputs("epm: Warning, no matching %if or %ifdef for %else.\n", stderr);
2014 	break;
2015       }
2016 
2017       if (*skip & SKIP_IFSAT)
2018         *skip |= SKIP_IF;
2019       else
2020       {
2021 	*skip &= ~SKIP_IF;
2022 	*skip |= SKIP_IFSAT;
2023       }
2024     }
2025     else if (!strcmp(buffer, "%endif\n"))
2026     {
2027      /*
2028       * Cancel any filtering based on environment variables.
2029       */
2030 
2031       if (!(*skip & SKIP_IFACTIVE))
2032       {
2033        /*
2034         * No matching %if/%ifdef statement...
2035 	*/
2036 
2037         fputs("epm: Warning, no matching %if or %ifdef for %endif.\n", stderr);
2038 	break;
2039       }
2040 
2041       *skip &= ~(SKIP_IF | SKIP_IFACTIVE | SKIP_IFSAT);
2042     }
2043     else if (!(*skip & SKIP_MASK))
2044     {
2045      /*
2046       * Otherwise strip any trailing newlines and return the string!
2047       */
2048 
2049       if (buffer[strlen(buffer) - 1] == '\n')
2050         buffer[strlen(buffer) - 1] = '\0';
2051 
2052       return (buffer);
2053     }
2054   }
2055 
2056   return (NULL);
2057 }
2058 
2059 
2060 /*
2061  * 'get_string()' - Get a delimited string from a line.
2062  */
2063 
2064 static char *				/* O  - String or NULL */
get_string(char ** src,char * dst,size_t dstsize)2065 get_string(char   **src,		/* IO - Source string */
2066            char   *dst,			/* O  - Destination string */
2067 	   size_t dstsize)		/* I  - Size of destination string */
2068 {
2069   char	*srcptr,			/* Current source pointer */
2070 	*dstptr,			/* Current destination pointer */
2071 	*dstend,			/* End of destination string */
2072 	quote;				/* Quoting char */
2073 
2074 
2075  /*
2076   * Initialize things...
2077   */
2078 
2079   srcptr = *src;
2080   dstptr = dst;
2081   dstend = dst + dstsize - 1;
2082 
2083   *dstptr = '\0';
2084 
2085  /*
2086   * Skip leading whitespace...
2087   */
2088 
2089   while (isspace(*srcptr & 255))
2090     srcptr ++;
2091 
2092   if (!*srcptr)
2093   {
2094     *src = srcptr;
2095 
2096     return (NULL);
2097   }
2098 
2099  /*
2100   * Grab the next string...
2101   */
2102 
2103   while (*srcptr && !isspace(*srcptr & 255))
2104   {
2105     if (*srcptr == '\\')
2106     {
2107       srcptr ++;
2108 
2109       if (!*srcptr)
2110       {
2111         fputs("epm: Expected character after backslash.\n", stderr);
2112 
2113         *src = srcptr;
2114         *dst = '\0';
2115 
2116 	return (NULL);
2117       }
2118     }
2119     else if (*srcptr == '\'' || *srcptr == '\"')
2120     {
2121      /*
2122       * Read a quoted string...
2123       */
2124 
2125       quote = *srcptr++;
2126 
2127       while (*srcptr != quote && *srcptr)
2128       {
2129         if (*srcptr == '\\')
2130 	{
2131 	  srcptr ++;
2132 
2133 	  if (!*srcptr)
2134 	  {
2135             fputs("epm: Expected character after backslash.\n", stderr);
2136 
2137             *src = srcptr;
2138             *dst = '\0';
2139 
2140 	    return (NULL);
2141 	  }
2142 	}
2143 
2144 	if (dstptr < dstend)
2145 	  *dstptr++ = *srcptr;
2146 
2147 	srcptr ++;
2148       }
2149 
2150       if (!*srcptr)
2151       {
2152         fprintf(stderr, "epm: Expected end quote %c.\n", quote);
2153 
2154         *src = srcptr;
2155 	*dst = '\0';
2156 
2157 	return (NULL);
2158       }
2159 
2160       srcptr ++;
2161       continue;
2162     }
2163 
2164     if (dstptr < dstend)
2165       *dstptr++ = *srcptr;
2166 
2167     srcptr ++;
2168   }
2169 
2170   *dstptr = '\0';
2171 
2172  /*
2173   * Skip leading whitespace...
2174   */
2175 
2176   while (isspace(*srcptr & 255))
2177     srcptr ++;
2178 
2179  /*
2180   * Return the string and string pointer...
2181   */
2182 
2183   *src = srcptr;
2184 
2185   return (dst);
2186 }
2187 
2188 
2189 /*
2190  * 'patmatch()' - Pattern matching...
2191  */
2192 
2193 static int				/* O - 1 if match, 0 if no match */
patmatch(const char * s,const char * pat)2194 patmatch(const char *s,			/* I - String to match against */
2195          const char *pat)		/* I - Pattern to match against */
2196 {
2197  /*
2198   * Range check the input...
2199   */
2200 
2201   if (s == NULL || pat == NULL)
2202     return (0);
2203 
2204  /*
2205   * Loop through the pattern and match strings, and stop if we come to a
2206   * point where the strings don't match or we find a complete match.
2207   */
2208 
2209   while (*s != '\0' && *pat != '\0')
2210   {
2211     if (*pat == '*')
2212     {
2213      /*
2214       * Wildcard - 0 or more characters...
2215       */
2216 
2217       pat ++;
2218       if (*pat == '\0')
2219         return (1);	/* Last pattern char is *, so everything matches now... */
2220 
2221      /*
2222       * Test all remaining combinations until we get to the end of the string.
2223       */
2224 
2225       while (*s != '\0')
2226       {
2227         if (patmatch(s, pat))
2228 	  return (1);
2229 
2230 	s ++;
2231       }
2232     }
2233     else if (*pat == '?')
2234     {
2235      /*
2236       * Wildcard - 1 character...
2237       */
2238 
2239       pat ++;
2240       s ++;
2241       continue;
2242     }
2243     else if (*pat == '[')
2244     {
2245      /*
2246       * Match a character from the input set [chars]...
2247       */
2248 
2249       pat ++;
2250       while (*pat != ']' && *pat != '\0')
2251         if (*s == *pat)
2252 	  break;
2253 	else if (pat[1] == '-' && *s >= pat[0] && *s <= pat[2])
2254 	  break;
2255 	else
2256 	  pat ++;
2257 
2258       if (*pat == ']' || *pat == '\0')
2259         return (0);
2260 
2261       while (*pat != ']' && *pat != '\0')
2262         pat ++;
2263 
2264       if (*pat == ']')
2265         pat ++;
2266 
2267       s ++;
2268 
2269       continue;
2270     }
2271     else if (*pat == '\\')
2272     {
2273      /*
2274       * Handle quoted characters...
2275       */
2276 
2277       pat ++;
2278     }
2279 
2280    /*
2281     * Stop if the pattern and string don't match...
2282     */
2283 
2284     if (*pat++ != *s++)
2285       return (0);
2286   }
2287 
2288  /*
2289   * If we are at the end of the string, see if the pattern remaining is
2290   * "*"...
2291   */
2292 
2293   while (*pat == '*')
2294     pat ++;
2295 
2296  /*
2297   * Done parsing the pattern and string; return 1 if the last character matches
2298   * and 0 otherwise...
2299   */
2300 
2301   return (*s == *pat);
2302 }
2303 
2304 
2305 /*
2306  * 'sort_subpackages()' - Compare two subpackage names.
2307  */
2308 
2309 static int				/* O - Result of comparison */
sort_subpackages(char ** a,char ** b)2310 sort_subpackages(char **a,		/* I - First subpackage */
2311                  char **b)		/* I - Second subpackage */
2312 {
2313   return (strcmp(*a, *b));
2314 }
2315 
2316 
2317 /*
2318  * 'update_architecture()' - Normalize the machine architecture name.
2319  */
2320 
2321 static void
update_architecture(char * buffer,size_t bufsize)2322 update_architecture(char   *buffer,	/* I - String buffer */
2323                     size_t bufsize)	/* I - Size of string buffer */
2324 {
2325   char	*temp;				/* Pointer into string buffer */
2326 
2327 
2328  /*
2329   * Convert the name to lowercase with no underscores or dashes.
2330   */
2331 
2332   for (temp = buffer; *temp != '\0'; temp ++)
2333     if (*temp == '-' || *temp == '_')
2334     {
2335       char *ptr;			/* Pointer into string */
2336 
2337       for (ptr = temp; ptr[1]; ptr ++)
2338         *ptr = ptr[1];
2339       *ptr = '\0';
2340 
2341       temp --;
2342     }
2343     else
2344       *temp = tolower(*temp);
2345 
2346  /*
2347   * Convert common synonyms to generic names...
2348   */
2349 
2350   if (strstr(buffer, "86"))
2351   {
2352     if (strstr(buffer, "64"))
2353       strlcpy(buffer, "x86_64", bufsize);
2354     else
2355       strlcpy(buffer, "intel", bufsize);
2356   }
2357   else if (!strncmp(buffer, "arm", 3))
2358     strlcpy(buffer, "arm", bufsize);
2359   else if (!strncmp(buffer, "ppc", 3))
2360     strlcpy(buffer, "powerpc", bufsize);
2361   else if (!strncmp(buffer, "sun", 3))
2362     strlcpy(buffer, "sparc", bufsize);
2363 }
2364