Subversion Repositories SvarDOS

Rev

Rev 927 | Rev 941 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 927 Rev 936
1
<?php /*
1
<?php /*
2
 
2
 
3
  SvarDOS repo index builder
3
  SvarDOS repo index builder
4
  Copyright (C) Mateusz Viste 2012-2022
4
  Copyright (C) Mateusz Viste 2012-2022
5
 
5
 
6
  buildidx computes an index json file for the SvarDOS repository.
6
  buildidx computes an index json file for the SvarDOS repository.
7
  it must be executed pointing to a directory that stores packages (*.svp)
7
  it must be executed pointing to a directory that stores packages (*.svp)
8
  files. buildidx will generate the index file and save it into the package
8
  files. buildidx will generate the index file and save it into the package
9
  repository.
9
  repository.
10
 
10
 
11
  requires php-zip
11
  requires php-zip
12
 
12
 
13
  21 feb 2022: buildidx collects categories looking at the dir layout of each package + improved version string parsing (replaced version_compare call by dos_version_compare)
13
  21 feb 2022: buildidx collects categories looking at the dir layout of each package + improved version string parsing (replaced version_compare call by dos_version_compare)
14
  17 feb 2022: checking for non-8+3 filenames in packages and duplicates + devload no longer part of CORE
14
  17 feb 2022: checking for non-8+3 filenames in packages and duplicates + devload no longer part of CORE
15
  16 feb 2022: added warning about overlong version strings and wild files location
15
  16 feb 2022: added warning about overlong version strings and wild files location
16
  15 feb 2022: index is generated as json, contains all filenames and alt versions
16
  15 feb 2022: index is generated as json, contains all filenames and alt versions
17
  14 feb 2022: packages are expected to have the *.svp extension
17
  14 feb 2022: packages are expected to have the *.svp extension
18
  12 feb 2022: skip source packages from being processed (*.src.zip)
18
  12 feb 2022: skip source packages from being processed (*.src.zip)
19
  20 jan 2022: rewritten the code from ANSI C to PHP for easier maintenance
19
  20 jan 2022: rewritten the code from ANSI C to PHP for easier maintenance
20
  13 feb 2021: 'title' LSM field is no longer looked after
20
  13 feb 2021: 'title' LSM field is no longer looked after
21
  11 feb 2021: lsm headers are no longer checked, so it is compatible with the simpler lsm format used by SvarDOS
21
  11 feb 2021: lsm headers are no longer checked, so it is compatible with the simpler lsm format used by SvarDOS
22
  13 jan 2021: removed the identification line, changed CRC32 to bsum, not creating the listing.txt file and stopped compressing index
22
  13 jan 2021: removed the identification line, changed CRC32 to bsum, not creating the listing.txt file and stopped compressing index
23
  23 apr 2017: uncompressed index is no longer created, added CRC32 of zib (bin only) files, if present
23
  23 apr 2017: uncompressed index is no longer created, added CRC32 of zib (bin only) files, if present
24
  28 aug 2016: listing.txt is always written inside the repo dir (instead of inside current dir)
24
  28 aug 2016: listing.txt is always written inside the repo dir (instead of inside current dir)
25
  27 aug 2016: accepting full paths to repos (starting with /...)
25
  27 aug 2016: accepting full paths to repos (starting with /...)
26
  07 dec 2013: rewritten buildidx in ANSI C89
26
  07 dec 2013: rewritten buildidx in ANSI C89
27
  19 aug 2013: add a compressed version of the index file to repos (index.gz)
27
  19 aug 2013: add a compressed version of the index file to repos (index.gz)
28
  22 jul 2013: creating a listing.txt file with list of packages
28
  22 jul 2013: creating a listing.txt file with list of packages
29
  18 jul 2013: writing the number of packaged into the first line of the lst file
29
  18 jul 2013: writing the number of packaged into the first line of the lst file
30
  11 jul 2013: added a switch to 7za to make it case insensitive when extracting lsm files
30
  11 jul 2013: added a switch to 7za to make it case insensitive when extracting lsm files
31
  10 jul 2013: changed unzip calls to 7za (to handle cases when appinfo is compressed with lzma)
31
  10 jul 2013: changed unzip calls to 7za (to handle cases when appinfo is compressed with lzma)
32
  04 feb 2013: added CRC32 support
32
  04 feb 2013: added CRC32 support
33
  22 sep 2012: forked 1st version from FDUPDATE builder
33
  22 sep 2012: forked 1st version from FDUPDATE builder
34
*/
34
*/
35
 
35
 
36
$PVER = "20220222";
36
$PVER = "20220222";
37
 
37
 
38
 
38
 
39
// computes the BSD sum of a file and returns it
39
// computes the BSD sum of a file and returns it
40
function file2bsum($fname) {
40
function file2bsum($fname) {
41
  $result = 0;
41
  $result = 0;
42
 
42
 
43
  $fd = fopen($fname, 'rb');
43
  $fd = fopen($fname, 'rb');
44
  if ($fd === false) return(0);
44
  if ($fd === false) return(0);
45
 
45
 
46
  while (!feof($fd)) {
46
  while (!feof($fd)) {
47
 
47
 
48
    $buff = fread($fd, 1024 * 1024);
48
    $buff = fread($fd, 1024 * 1024);
49
 
49
 
50
    $slen = strlen($buff);
50
    $slen = strlen($buff);
51
    for ($i = 0; $i < $slen; $i++) {
51
    for ($i = 0; $i < $slen; $i++) {
52
      // rotr
52
      // rotr
53
      $result = ($result >> 1) | ($result << 15);
53
      $result = ($result >> 1) | ($result << 15);
54
      // add and truncate to 16 bits
54
      // add and truncate to 16 bits
55
      $result += ord($buff[$i]);
55
      $result += ord($buff[$i]);
56
      $result &= 0xffff;
56
      $result &= 0xffff;
57
    }
57
    }
58
  }
58
  }
59
 
59
 
60
  fclose($fd);
60
  fclose($fd);
61
  return($result);
61
  return($result);
62
}
62
}
63
 
63
 
64
 
64
 
65
// translates a version string into a array of integer values.
65
// translates a version string into a array of integer values.
66
// Accepted formats follow:
66
// Accepted formats follow:
67
//    300.12.1
67
//    300.12.1
68
//    1
68
//    1
69
//    12.2.34.2-4.5
69
//    12.2.34.2-4.5
70
//    1.2c
70
//    1.2c
71
//    1.01 beta+3
71
//    1.01 beta+3
72
//    2013-12-31
72
//    2013-12-31
73
//    20220222 alpha
73
//    20220222 alpha
74
function vertoarr($verstr) {
74
function vertoarr($verstr) {
75
  $subver = array(0,0,0,0);
75
  $subver = array(0,0,0,0);
76
 
76
 
77
  // switch string to lcase for easier processing and trim any leading or trailing white spaces
77
  // switch string to lcase for easier processing and trim any leading or trailing white spaces
78
  $verstr = strtolower(trim($verstr));
78
  $verstr = strtolower(trim($verstr));
79
 
79
 
80
  // replace all '-' and '/' characters to '.' (uniformization of sub-version parts delimiters)
80
  // replace all '-' and '/' characters to '.' (uniformization of sub-version parts delimiters)
81
  $verstr = strtr($verstr, '-/', '..');
81
  $verstr = strtr($verstr, '-/', '..');
82
 
82
 
83
  // is there a subversion value? (for example "+4" in "1.05+4")
83
  // is there a subversion value? (for example "+4" in "1.05+4")
84
  $i = strrpos($verstr, '+', 1);
84
  $i = strrpos($verstr, '+', 1);
85
  if ($i !== false) {
85
  if ($i !== false) {
86
    // validate the svar-version is a proper integer
86
    // validate the svar-version is a proper integer
87
    $svarver = substr($verstr, $i + 1);
87
    $svarver = substr($verstr, $i + 1);
88
    if (! preg_match('/[1-9][0-9]*/', $svarver)) {
88
    if (! preg_match('/[1-9][0-9]*/', $svarver)) {
89
      return(false);
89
      return(false);
90
    }
90
    }
91
    $subver[3] = intval($svarver); // set the +rev as a very minor item
91
    $subver[3] = intval($svarver); // set the +rev as a very minor item
92
    $verstr = substr($verstr, 0, $i);
92
    $verstr = substr($verstr, 0, $i);
93
  }
93
  }
94
 
94
 
-
 
95
  // beta reordering: convert "beta 0.95" to "0.95 beta"
-
 
96
  if (preg_match('/^beta /', $verstr)) $verstr = substr($verstr, 5) . ' beta';
-
 
97
 
95
  // any occurence of alpha,beta,gamma,delta etc preceded by a digit should have a space separator added
98
  // any occurence of alpha,beta,gamma,delta etc preceded by a digit should have a space separator added
96
  // example: "2.6.0pre9" becomes "2.6.0 pre9"
99
  // example: "2.6.0pre9" becomes "2.6.0 pre9"
97
  $verstr = preg_replace('/([0-9])(alpha|beta|gamma|delta|pre|rc|patch)/', '$1 $2', $verstr);
100
  $verstr = preg_replace('/([0-9])(alpha|beta|gamma|delta|pre|rc|patch)/', '$1 $2', $verstr);
98
 
101
 
99
  // same as above, but this time adding a trailing space separator
102
  // same as above, but this time adding a trailing space separator
100
  // example: "2.6.0 pre9" becomes "2.6.0 pre 9"
103
  // example: "2.6.0 pre9" becomes "2.6.0 pre 9"
101
  $verstr = preg_replace('/(alpha|beta|gamma|delta|pre|rc|patch)([0-9])/', '$1 $2', $verstr);
104
  $verstr = preg_replace('/(alpha|beta|gamma|delta|pre|rc|patch)([0-9])/', '$1 $2', $verstr);
102
 
105
 
103
  // is the version ending with ' alpha', 'beta', etc?
106
  // is the version ending with ' alpha', 'beta', etc?
104
  if (preg_match('/ (alpha|beta|gamma|delta|pre|rc|patch)( [0-9]{1,4}){0,1}$/', $verstr)) {
107
  if (preg_match('/ (alpha|beta|gamma|delta|pre|rc|patch)( [0-9]{1,4}){0,1}$/', $verstr)) {
105
    // if there is a trailing beta-number, process it first
108
    // if there is a trailing beta-number, process it first
106
    if (preg_match('/ [0-9]{1,4}$/', $verstr)) {
109
    if (preg_match('/ [0-9]{1,4}$/', $verstr)) {
107
      $i = strrpos($verstr, ' ');
110
      $i = strrpos($verstr, ' ');
108
      $subver[2] = intval(substr($verstr, $i + 1));
111
      $subver[2] = intval(substr($verstr, $i + 1));
109
      $verstr = trim(substr($verstr, 0, $i));
112
      $verstr = trim(substr($verstr, 0, $i));
110
    }
113
    }
111
    $i = strrpos($verstr, ' ');
114
    $i = strrpos($verstr, ' ');
112
    $greek = substr($verstr, $i + 1);
115
    $greek = substr($verstr, $i + 1);
113
    $verstr = trim(substr($verstr, 0, $i));
116
    $verstr = trim(substr($verstr, 0, $i));
114
    if ($greek == 'alpha') {
117
    if ($greek == 'alpha') {
115
      $subver[1] = 1;
118
      $subver[1] = 1;
116
    } else if ($greek == 'beta') {
119
    } else if ($greek == 'beta') {
117
      $subver[1] = 2;
120
      $subver[1] = 2;
118
    } else if ($greek == 'gamma') {
121
    } else if ($greek == 'gamma') {
119
      $subver[1] = 3;
122
      $subver[1] = 3;
120
    } else if ($greek == 'delta') {
123
    } else if ($greek == 'delta') {
121
      $subver[1] = 4;
124
      $subver[1] = 4;
122
    } else if ($greek == 'pre') {
125
    } else if ($greek == 'pre') {
123
      $subver[1] = 5;
126
      $subver[1] = 5;
124
    } else if ($greek == 'rc') {
127
    } else if ($greek == 'rc') {
125
      $subver[1] = 6;
128
      $subver[1] = 6;
126
    } else if ($greek == 'patch') { // this is a POST-release version, as opposed to all above that are PRE-release versions
129
    } else if ($greek == 'patch') { // this is a POST-release version, as opposed to all above that are PRE-release versions
127
      $subver[1] = 99;
130
      $subver[1] = 99;
128
    } else {
131
    } else {
129
      return(false);
132
      return(false);
130
    }
133
    }
131
  } else {
134
  } else {
132
    $subver[1] = 98; // one less than the 'patch' level
135
    $subver[1] = 98; // one less than the 'patch' level
133
  }
136
  }
134
 
137
 
135
  // does the version string have a single-letter subversion? (1.0c)
138
  // does the version string have a single-letter subversion? (1.0c)
136
  if (preg_match('/[a-z]$/', $verstr)) {
139
  if (preg_match('/[a-z]$/', $verstr)) {
137
    $subver[0] = ord(substr($verstr, -1));
140
    $subver[0] = ord(substr($verstr, -1));
138
    $verstr = substr_replace($verstr, '', -1); // remove last character from string
141
    $verstr = substr_replace($verstr, '', -1); // remove last character from string
139
  }
142
  }
140
 
143
 
141
  // convert "30-jan-99", "1999-jan-30" and "30-jan-1999" versions to "30jan99" or "30jan1999"
144
  // convert "30-jan-99", "1999-jan-30" and "30-jan-1999" versions to "30jan99" or "30jan1999"
142
  // note that dashes have already been replaced by dots
145
  // note that dashes have already been replaced by dots
143
  if (preg_match('/^([0-9][0-9]){1,2}\.(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)\.([0-9][0-9]){1,2}$/', $verstr)) {
146
  if (preg_match('/^([0-9][0-9]){1,2}\.(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)\.([0-9][0-9]){1,2}$/', $verstr)) {
144
    $verstr = str_replace('.', '', $verstr);
147
    $verstr = str_replace('.', '', $verstr);
145
  }
148
  }
146
 
149
 
147
  // convert "2009mar17" versions to "17mar2009"
150
  // convert "2009mar17" versions to "17mar2009"
148
  if (preg_match('/^[0-9]{4}(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)[0-9]{2}$/', $verstr)) {
151
  if (preg_match('/^[0-9]{4}(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)[0-9]{2}$/', $verstr)) {
149
    $dy = substr($verstr, 7);
152
    $dy = substr($verstr, 7);
150
    $mo = substr($verstr, 4, 3);
153
    $mo = substr($verstr, 4, 3);
151
    $ye = substr($verstr, 0, 4);
154
    $ye = substr($verstr, 0, 4);
152
    $verstr = "{$dy}{$mo}{$ye}";
155
    $verstr = "{$dy}{$mo}{$ye}";
153
  }
156
  }
154
 
157
 
155
  // convert "30jan99" versions to 99.1.30 and "30jan1999" to 1999.1.30
158
  // convert "30jan99" versions to 99.1.30 and "30jan1999" to 1999.1.30
156
  if (preg_match('/^[0-3][0-9](jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)([0-9][0-9]){1,2}$/', $verstr)) {
159
  if (preg_match('/^[0-3][0-9](jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)([0-9][0-9]){1,2}$/', $verstr)) {
157
    $months = array('jan' => 1, 'feb' => 2, 'mar' => 3, 'apr' => 4, 'may' => 5, 'jun' => 6, 'jul' => 7, 'aug' => 8, 'sep' => 9, 'oct' => 10, 'nov' => 11, 'dec' => 12);
160
    $months = array('jan' => 1, 'feb' => 2, 'mar' => 3, 'apr' => 4, 'may' => 5, 'jun' => 6, 'jul' => 7, 'aug' => 8, 'sep' => 9, 'oct' => 10, 'nov' => 11, 'dec' => 12);
158
    $dy = substr($verstr, 0, 2);
161
    $dy = substr($verstr, 0, 2);
159
    $mo = $months[substr($verstr, 2, 3)];
162
    $mo = $months[substr($verstr, 2, 3)];
160
    $ye = substr($verstr, 5);
163
    $ye = substr($verstr, 5);
161
    $verstr = "{$ye}.{$mo}.{$dy}";
164
    $verstr = "{$ye}.{$mo}.{$dy}";
162
  }
165
  }
163
 
166
 
164
  // validate the format is supported, should be something no more complex than 1.05.3.33
167
  // validate the format is supported, should be something no more complex than 1.05.3.33
165
  if (! preg_match('/^[0-9][0-9.]{0,20}$/', $verstr)) {
168
  if (! preg_match('/^[0-9][0-9.]{0,20}$/', $verstr)) {
166
    return(false);
169
    return(false);
167
  }
170
  }
168
 
171
 
169
  // NOTE: a zero right after a separator and trailed with a digit (as in 1.01)
172
  // NOTE: a zero right after a separator and trailed with a digit (as in 1.01)
170
  //       has a special meaning
173
  //       has a special meaning
171
  $exploded = explode('.', $verstr);
174
  $exploded = explode('.', $verstr);
172
  if (count($exploded) > 16) {
175
  if (count($exploded) > 16) {
173
    return(false);
176
    return(false);
174
  }
177
  }
175
  $exploded[16] = $subver[0]; // a-z (1.0c)
178
  $exploded[16] = $subver[0]; // a-z (1.0c)
176
  $exploded[17] = $subver[1]; // alpha/beta/gamma/delta/rc/pre
179
  $exploded[17] = $subver[1]; // alpha/beta/gamma/delta/rc/pre
177
  $exploded[18] = $subver[2]; // alpha-beta-gamma subversion (eg. "beta 9")
180
  $exploded[18] = $subver[2]; // alpha-beta-gamma subversion (eg. "beta 9")
178
  $exploded[19] = $subver[3]; // svar-ver (1.0+5)
181
  $exploded[19] = $subver[3]; // svar-ver (1.0+5)
179
  for ($i = 0; $i < 20; $i++) if (empty($exploded[$i])) $exploded[$i] = '0';
182
  for ($i = 0; $i < 20; $i++) if (empty($exploded[$i])) $exploded[$i] = '0';
180
 
183
 
181
  ksort($exploded);
184
  ksort($exploded);
182
 
185
 
183
  return($exploded);
186
  return($exploded);
184
}
187
}
185
 
188
 
186
 
189
 
187
function dos_version_compare($v1, $v2) {
190
function dos_version_compare($v1, $v2) {
188
  $v1arr = vertoarr($v1);
191
  $v1arr = vertoarr($v1);
189
  $v2arr = vertoarr($v2);
192
  $v2arr = vertoarr($v2);
190
  for ($i = 0; $i < count($v1arr); $i++) {
193
  for ($i = 0; $i < count($v1arr); $i++) {
191
    if ($v1arr[$i] > $v2arr[$i]) return(1);
194
    if ($v1arr[$i] > $v2arr[$i]) return(1);
192
    if ($v1arr[$i] < $v2arr[$i]) return(-1);
195
    if ($v1arr[$i] < $v2arr[$i]) return(-1);
193
  }
196
  }
194
  return(0);
197
  return(0);
195
}
198
}
196
 
199
 
197
 
200
 
198
// reads file fil from zip archive z and returns its content, or false on error
201
// reads file fil from zip archive z and returns its content, or false on error
199
function read_file_from_zip($z, $fil) {
202
function read_file_from_zip($z, $fil) {
200
  $zip = new ZipArchive;
203
  $zip = new ZipArchive;
201
  if ($zip->open($z, ZipArchive::RDONLY) !== true) {
204
  if ($zip->open($z, ZipArchive::RDONLY) !== true) {
202
    echo "ERROR: failed to open zip file '{$z}'\n";
205
    echo "ERROR: failed to open zip file '{$z}'\n";
203
    return(false);
206
    return(false);
204
  }
207
  }
205
 
208
 
206
  // load the appinfo/pkgname.lsm file
209
  // load the appinfo/pkgname.lsm file
207
  $res = $zip->getFromName($fil, 8192, ZipArchive::FL_NOCASE);
210
  $res = $zip->getFromName($fil, 8192, ZipArchive::FL_NOCASE);
208
 
211
 
209
  $zip->close();
212
  $zip->close();
210
  return($res);
213
  return($res);
211
}
214
}
212
 
215
 
213
 
216
 
214
function read_list_of_files_in_zip($z) {
217
function read_list_of_files_in_zip($z) {
215
  $zip = new ZipArchive;
218
  $zip = new ZipArchive;
216
  if ($zip->open($z, ZipArchive::RDONLY) !== true) {
219
  if ($zip->open($z, ZipArchive::RDONLY) !== true) {
217
    echo "ERROR: failed to open zip file '{$z}'\n";
220
    echo "ERROR: failed to open zip file '{$z}'\n";
218
    return(false);
221
    return(false);
219
  }
222
  }
220
 
223
 
221
  $res = array();
224
  $res = array();
222
  for ($i = 0; $i < $zip->numFiles; $i++) $res[] = $zip->getNameIndex($i);
225
  for ($i = 0; $i < $zip->numFiles; $i++) $res[] = $zip->getNameIndex($i);
223
 
226
 
224
  $zip->close();
227
  $zip->close();
225
  return($res);
228
  return($res);
226
}
229
}
227
 
230
 
228
 
231
 
229
// reads a LSM string and returns it in the form of an array
232
// reads a LSM string and returns it in the form of an array
230
function parse_lsm($s) {
233
function parse_lsm($s) {
231
  $res = array();
234
  $res = array();
232
  for ($l = strtok($s, "\n"); $l !== false; $l = strtok("\n")) {
235
  for ($l = strtok($s, "\n"); $l !== false; $l = strtok("\n")) {
233
    // the line is "token: value", let's find the colon
236
    // the line is "token: value", let's find the colon
234
    $colpos = strpos($l, ':');
237
    $colpos = strpos($l, ':');
235
    if (($colpos === false) || ($colpos === 0)) continue;
238
    if (($colpos === false) || ($colpos === 0)) continue;
236
    $tok = strtolower(trim(substr($l, 0, $colpos)));
239
    $tok = strtolower(trim(substr($l, 0, $colpos)));
237
    $val = trim(substr($l, $colpos + 1));
240
    $val = trim(substr($l, $colpos + 1));
238
    $res[$tok] = $val;
241
    $res[$tok] = $val;
239
  }
242
  }
240
  return($res);
243
  return($res);
241
}
244
}
242
 
245
 
243
 
246
 
244
// on PHP 8+ there is str_starts_with(), but not on PHP 7 so I use this
247
// on PHP 8+ there is str_starts_with(), but not on PHP 7 so I use this
245
function str_head_is($haystack, $needle) {
248
function str_head_is($haystack, $needle) {
246
  return strpos($haystack, $needle) === 0;
249
  return strpos($haystack, $needle) === 0;
247
}
250
}
248
 
251
 
249
 
252
 
250
// returns an array that contains CORE packages (populated from the core subdirectory in pkgdir)
253
// returns an array that contains CORE packages (populated from the core subdirectory in pkgdir)
251
function load_core_list($repodir) {
254
function load_core_list($repodir) {
252
  $res = array();
255
  $res = array();
253
 
256
 
254
  foreach (scandir($repodir . '/core/') as $f) {
257
  foreach (scandir($repodir . '/core/') as $f) {
255
    if (!preg_match('/\.svp$/', $f)) continue;
258
    if (!preg_match('/\.svp$/', $f)) continue;
256
    $res[] = explode('.', $f)[0];
259
    $res[] = explode('.', $f)[0];
257
  }
260
  }
258
  return($res);
261
  return($res);
259
}
262
}
260
 
263
 
261
 
264
 
262
// ***************** MAIN ROUTINE *********************************************
265
// ***************** MAIN ROUTINE *********************************************
263
 
266
 
264
//echo "SvarDOS repository index generator ver {$PVER}\n";
267
//echo "SvarDOS repository index generator ver {$PVER}\n";
265
 
268
 
266
if (($_SERVER['argc'] != 2) || ($_SERVER['argv'][1][0] == '-')) {
269
if (($_SERVER['argc'] != 2) || ($_SERVER['argv'][1][0] == '-')) {
267
  echo "usage: php buildidx.php repodir\n";
270
  echo "usage: php buildidx.php repodir\n";
268
  exit(1);
271
  exit(1);
269
}
272
}
270
 
273
 
271
$repodir = $_SERVER['argv'][1];
274
$repodir = $_SERVER['argv'][1];
272
 
275
 
273
$pkgfiles = scandir($repodir);
276
$pkgfiles = scandir($repodir);
274
$pkgcount = 0;
277
$pkgcount = 0;
275
 
278
 
276
 
279
 
277
// load the list of CORE and MSDOS_COMPAT packages
280
// load the list of CORE and MSDOS_COMPAT packages
278
 
281
 
279
$core_packages_list = load_core_list($repodir);
282
$core_packages_list = load_core_list($repodir);
280
$msdos_compat_list = explode(' ', 'append assign attrib chkdsk choice command comp cpidos debug defrag deltree diskcomp diskcopy display edit edlin exe2bin fc fdapm fdisk find format help himemx kernel keyb label localcfg mem mirror mode more move nlsfunc print replace share shsucdx sort swsubst tree undelete unformat xcopy');
283
$msdos_compat_list = explode(' ', 'append assign attrib chkdsk choice command comp cpidos debug defrag deltree diskcomp diskcopy display edit edlin exe2bin fc fdapm fdisk find format help himemx kernel keyb label localcfg mem mirror mode more move nlsfunc print replace share shsucdx sort swsubst tree undelete unformat xcopy');
281
 
284
 
282
// do a list of all svp packages with their available versions and descriptions
285
// do a list of all svp packages with their available versions and descriptions
283
 
286
 
284
$pkgdb = array();
287
$pkgdb = array();
285
foreach ($pkgfiles as $fname) {
288
foreach ($pkgfiles as $fname) {
286
  if (!preg_match('/\.svp$/i', $fname)) continue; // skip non-svp files
289
  if (!preg_match('/\.svp$/i', $fname)) continue; // skip non-svp files
287
 
290
 
288
  if (!preg_match('/^[a-zA-Z0-9+. _-]*\.svp$/', $fname)) {
291
  if (!preg_match('/^[a-zA-Z0-9+. _-]*\.svp$/', $fname)) {
289
    echo "ERROR: {$fname} has a very weird name\n";
292
    echo "ERROR: {$fname} has a very weird name\n";
290
    continue;
293
    continue;
291
  }
294
  }
292
 
295
 
293
  $path_parts = pathinfo($fname);
296
  $path_parts = pathinfo($fname);
294
  $pkgnam = explode('-', $path_parts['filename'])[0];
297
  $pkgnam = explode('-', $path_parts['filename'])[0];
295
  $pkgfullpath = realpath($repodir . '/' . $fname);
298
  $pkgfullpath = realpath($repodir . '/' . $fname);
296
 
299
 
297
  $lsm = read_file_from_zip($pkgfullpath, "appinfo/{$pkgnam}.lsm");
300
  $lsm = read_file_from_zip($pkgfullpath, "appinfo/{$pkgnam}.lsm");
298
  if ($lsm == false) {
301
  if ($lsm == false) {
299
    echo "ERROR: {$fname} does not contain an LSM file at the expected location\n";
302
    echo "ERROR: {$fname} does not contain an LSM file at the expected location\n";
300
    continue;
303
    continue;
301
  }
304
  }
302
  $lsmarray = parse_lsm($lsm);
305
  $lsmarray = parse_lsm($lsm);
303
  if (empty($lsmarray['version'])) {
306
  if (empty($lsmarray['version'])) {
304
    echo "ERROR: lsm file in {$fname} does not contain a version\n";
307
    echo "ERROR: lsm file in {$fname} does not contain a version\n";
305
    continue;
308
    continue;
306
  }
309
  }
307
  if (strlen($lsmarray['version']) > 16) {
310
  if (strlen($lsmarray['version']) > 16) {
308
    echo "ERROR: version string in lsm file of {$fname} is too long (16 chars max)\n";
311
    echo "ERROR: version string in lsm file of {$fname} is too long (16 chars max)\n";
309
    continue;
312
    continue;
310
  }
313
  }
311
  if (empty($lsmarray['description'])) {
314
  if (empty($lsmarray['description'])) {
312
    echo "ERROR: lsm file in {$fname} does not contain a description\n";
315
    echo "ERROR: lsm file in {$fname} does not contain a description\n";
313
    continue;
316
    continue;
314
  }
317
  }
315
 
318
 
316
  // validate the files present in the archive
319
  // validate the files present in the archive
317
  $listoffiles = read_list_of_files_in_zip($pkgfullpath);
320
  $listoffiles = read_list_of_files_in_zip($pkgfullpath);
318
  $pkgdir = $pkgnam;
321
  $pkgdir = $pkgnam;
319
 
322
 
320
  // special rule for "parent and children" packages
323
  // special rule for "parent and children" packages
321
  if (str_head_is($pkgnam, 'djgpp_')) $pkgdir = 'djgpp'; // djgpp_* packages put their files in djgpp
324
  if (str_head_is($pkgnam, 'djgpp_')) $pkgdir = 'djgpp'; // djgpp_* packages put their files in djgpp
322
  if ($pkgnam == 'fbc_help') $pkgdir = 'fbc'; // FreeBASIC help goes to the FreeBASIC dir
325
  if ($pkgnam == 'fbc_help') $pkgdir = 'fbc'; // FreeBASIC help goes to the FreeBASIC dir
323
  if ($pkgnam == 'clamdb') $pkgdir = 'clamav'; // data patterns for clamav
326
  if ($pkgnam == 'clamdb') $pkgdir = 'clamav'; // data patterns for clamav
324
 
327
 
325
  // array used to detect duplicated entries after lower-case conversion
328
  // array used to detect duplicated entries after lower-case conversion
326
  $duparr = array();
329
  $duparr = array();
327
 
330
 
328
  // will hold the list of categories that this package belongs to
331
  // will hold the list of categories that this package belongs to
329
  $catlist = array();
332
  $catlist = array();
330
 
333
 
331
  foreach ($listoffiles as $f) {
334
  foreach ($listoffiles as $f) {
332
    $f = strtolower($f);
335
    $f = strtolower($f);
333
    $path_array = explode('/', $f);
336
    $path_array = explode('/', $f);
334
    // emit a warning when non-8+3 filenames are spotted and find duplicates
337
    // emit a warning when non-8+3 filenames are spotted and find duplicates
335
    foreach ($path_array as $item) {
338
    foreach ($path_array as $item) {
336
      if (empty($item)) continue; // skip empty items at end of paths (eg. appinfo/)
339
      if (empty($item)) continue; // skip empty items at end of paths (eg. appinfo/)
337
      if (!preg_match("/[a-z0-9!#$%&'()@^_`{}~-]{1,8}(\.[a-z0-9!#$%&'()@^_`{}~-]{1,3}){0,1}/", $item)) {
340
      if (!preg_match("/[a-z0-9!#$%&'()@^_`{}~-]{1,8}(\.[a-z0-9!#$%&'()@^_`{}~-]{1,3}){0,1}/", $item)) {
338
        echo "WARNING: {$fname} contains a non-8+3 path (or weird char): {$item} (in $f)\n";
341
        echo "WARNING: {$fname} contains a non-8+3 path (or weird char): {$item} (in $f)\n";
339
      }
342
      }
340
    }
343
    }
341
    // look for dups
344
    // look for dups
342
    if (array_search($f, $duparr) !== false) {
345
    if (array_search($f, $duparr) !== false) {
343
      echo "WARNING: {$fname} contains a duplicated entry: '{$f}'\n";
346
      echo "WARNING: {$fname} contains a duplicated entry: '{$f}'\n";
344
    } else {
347
    } else {
345
      $duparr[] = $f;
348
      $duparr[] = $f;
346
    }
349
    }
347
    // LSM file is ok
350
    // LSM file is ok
348
    if ($f === "appinfo/{$pkgnam}.lsm") continue;
351
    if ($f === "appinfo/{$pkgnam}.lsm") continue;
349
    if ($f === "appinfo/") continue;
352
    if ($f === "appinfo/") continue;
350
    // CORE and MSDOS_COMPAT packages are premium citizens and can do a little more
353
    // CORE and MSDOS_COMPAT packages are premium citizens and can do a little more
351
    $core_or_msdoscompat = 0;
354
    $core_or_msdoscompat = 0;
352
    if (array_search($pkgnam, $core_packages_list) !== false) {
355
    if (array_search($pkgnam, $core_packages_list) !== false) {
353
      $catlist[] = 'core';
356
      $catlist[] = 'core';
354
      $core_or_msdoscompat = 1;
357
      $core_or_msdoscompat = 1;
355
    }
358
    }
356
    if (array_search($pkgnam, $msdos_compat_list) !== false) {
359
    if (array_search($pkgnam, $msdos_compat_list) !== false) {
357
      $catlist[] = 'msdos_compat';
360
      $catlist[] = 'msdos_compat';
358
      $core_or_msdoscompat = 1;
361
      $core_or_msdoscompat = 1;
359
    }
362
    }
360
    if ($core_or_msdoscompat == 1) {
363
    if ($core_or_msdoscompat == 1) {
361
      if (str_head_is($f, 'bin/')) continue;
364
      if (str_head_is($f, 'bin/')) continue;
362
      if (str_head_is($f, 'cpi/')) continue;
365
      if (str_head_is($f, 'cpi/')) continue;
363
      if (str_head_is($f, "doc/{$pkgdir}/")) continue;
366
      if (str_head_is($f, "doc/{$pkgdir}/")) continue;
364
      if ($f === 'doc/') continue;
367
      if ($f === 'doc/') continue;
365
      if (str_head_is($f, "nls/{$pkgdir}.")) continue;
368
      if (str_head_is($f, "nls/{$pkgdir}.")) continue;
366
      if ($f === 'nls/') continue;
369
      if ($f === 'nls/') continue;
367
    }
370
    }
368
    // the help package is allowed to put files in... help
371
    // the help package is allowed to put files in... help
369
    if (($pkgnam == 'help') && (str_head_is($f, 'help/'))) continue;
372
    if (($pkgnam == 'help') && (str_head_is($f, 'help/'))) continue;
370
    // must be category-prefixed file, add it to the list of categories for this package
373
    // must be category-prefixed file, add it to the list of categories for this package
371
    $catlist[] = explode('/', $f)[0];
374
    $catlist[] = explode('/', $f)[0];
372
    // well-known "category" dirs are okay
375
    // well-known "category" dirs are okay
373
    if (str_head_is($f, "progs/{$pkgdir}/")) continue;
376
    if (str_head_is($f, "progs/{$pkgdir}/")) continue;
374
    if ($f === 'progs/') continue;
377
    if ($f === 'progs/') continue;
375
    if (str_head_is($f, "devel/{$pkgdir}/")) continue;
378
    if (str_head_is($f, "devel/{$pkgdir}/")) continue;
376
    if ($f === 'devel/') continue;
379
    if ($f === 'devel/') continue;
377
    if (str_head_is($f, "games/{$pkgdir}/")) continue;
380
    if (str_head_is($f, "games/{$pkgdir}/")) continue;
378
    if ($f === 'games/') continue;
381
    if ($f === 'games/') continue;
379
    if (str_head_is($f, "drivers/{$pkgdir}/")) continue;
382
    if (str_head_is($f, "drivers/{$pkgdir}/")) continue;
380
    if ($f === 'drivers/') continue;
383
    if ($f === 'drivers/') continue;
381
    echo "WARNING: {$fname} contains a file in an illegal location: {$f}\n";
384
    echo "WARNING: {$fname} contains a file in an illegal location: {$f}\n";
382
  }
385
  }
383
 
386
 
384
  // do I understand the version string?
387
  // do I understand the version string?
385
  if (vertoarr($lsmarray['version']) === false) echo "WARNING: {$fname} parsing of version string failed ('{$lsmarray['version']}')\n";
388
  if (vertoarr($lsmarray['version']) === false) echo "WARNING: {$fname} parsing of version string failed ('{$lsmarray['version']}')\n";
386
 
389
 
387
  $meta['fname'] = $fname;
390
  $meta['fname'] = $fname;
388
  $meta['desc'] = $lsmarray['description'];
391
  $meta['desc'] = $lsmarray['description'];
389
  $meta['cats'] = array_unique($catlist);
392
  $meta['cats'] = array_unique($catlist);
390
 
393
 
391
  $pkgdb[$pkgnam][$lsmarray['version']] = $meta;
394
  $pkgdb[$pkgnam][$lsmarray['version']] = $meta;
392
}
395
}
393
 
396
 
394
 
397
 
395
$db = array();
398
$db = array();
396
$cats = array();
399
$cats = array();
397
 
400
 
398
// ******** compute the version-sorted list of packages with a single *********
401
// ******** compute the version-sorted list of packages with a single *********
399
// ******** description and category list for each package ********************
402
// ******** description and category list for each package ********************
400
 
403
 
401
// iterate over each svp package
404
// iterate over each svp package
402
foreach ($pkgdb as $pkg => $versions) {
405
foreach ($pkgdb as $pkg => $versions) {
403
 
406
 
404
  // sort filenames by version, highest first
407
  // sort filenames by version, highest first
405
  uksort($versions, "dos_version_compare");
408
  uksort($versions, "dos_version_compare");
406
  $versions = array_reverse($versions, true);
409
  $versions = array_reverse($versions, true);
407
 
410
 
408
  foreach ($versions as $ver => $meta) {
411
  foreach ($versions as $ver => $meta) {
409
    $fname = $meta['fname'];
412
    $fname = $meta['fname'];
410
    $desc = $meta['desc'];
413
    $desc = $meta['desc'];
411
 
414
 
412
    $bsum = file2bsum(realpath($repodir . '/' . $fname));
415
    $bsum = file2bsum(realpath($repodir . '/' . $fname));
413
 
416
 
414
    $meta2['ver'] = strval($ver);
417
    $meta2['ver'] = strval($ver);
415
    $meta2['bsum'] = $bsum;
418
    $meta2['bsum'] = $bsum;
416
 
419
 
417
    if (empty($db[$pkg]['desc'])) $db[$pkg]['desc'] = $desc;
420
    if (empty($db[$pkg]['desc'])) $db[$pkg]['desc'] = $desc;
418
    if (empty($db[$pkg]['cats'])) {
421
    if (empty($db[$pkg]['cats'])) {
419
      $db[$pkg]['cats'] = $meta['cats'];
422
      $db[$pkg]['cats'] = $meta['cats'];
420
      $cats = array_unique(array_merge($cats, $meta['cats']));
423
      $cats = array_unique(array_merge($cats, $meta['cats']));
421
    }
424
    }
422
    $db[$pkg]['versions'][$fname] = $meta2;
425
    $db[$pkg]['versions'][$fname] = $meta2;
423
  }
426
  }
424
 
427
 
425
  $pkgcount++;
428
  $pkgcount++;
426
 
429
 
427
}
430
}
428
 
431
 
429
if ($pkgcount < 100) echo "WARNING: an unexpectedly low number of packages has been found in the repo ({$pkgcount})\n";
432
if ($pkgcount < 100) echo "WARNING: an unexpectedly low number of packages has been found in the repo ({$pkgcount})\n";
430
 
433
 
431
$json_blob = json_encode($db);
434
$json_blob = json_encode($db);
432
if ($json_blob === false) {
435
if ($json_blob === false) {
433
  echo "ERROR: JSON convertion failed! -> ";
436
  echo "ERROR: JSON convertion failed! -> ";
434
  switch (json_last_error()) {
437
  switch (json_last_error()) {
435
    case JSON_ERROR_DEPTH:
438
    case JSON_ERROR_DEPTH:
436
      echo 'maximum stack depth exceeded';
439
      echo 'maximum stack depth exceeded';
437
      break;
440
      break;
438
    case JSON_ERROR_STATE_MISMATCH:
441
    case JSON_ERROR_STATE_MISMATCH:
439
      echo 'underflow of the modes mismatch';
442
      echo 'underflow of the modes mismatch';
440
      break;
443
      break;
441
    case JSON_ERROR_CTRL_CHAR:
444
    case JSON_ERROR_CTRL_CHAR:
442
      echo 'unexpected control character found';
445
      echo 'unexpected control character found';
443
      break;
446
      break;
444
    case JSON_ERROR_UTF8:
447
    case JSON_ERROR_UTF8:
445
      echo 'malformed utf-8 characters';
448
      echo 'malformed utf-8 characters';
446
      break;
449
      break;
447
    default:
450
    default:
448
      echo "unknown error";
451
      echo "unknown error";
449
      break;
452
      break;
450
  }
453
  }
451
  echo "\n";
454
  echo "\n";
452
}
455
}
453
 
456
 
454
file_put_contents($repodir . '/_index.json', $json_blob);
457
file_put_contents($repodir . '/_index.json', $json_blob);
455
 
458
 
456
$cats_json = json_encode($cats);
459
$cats_json = json_encode($cats);
457
file_put_contents($repodir . '/_cats.json', $cats_json);
460
file_put_contents($repodir . '/_cats.json', $cats_json);
458
 
461
 
459
exit(0);
462
exit(0);
460
 
463
 
461
?>
464
?>
462
 
465