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