]> git.gag.com Git - hw/altusmetrum/blob - bin/partslist-vendor
IPC standard for describing component orientations in XYRS data
[hw/altusmetrum] / bin / partslist-vendor
1 #!/usr/bin/env nickle
2 #
3 # Convert CSV parts list into vendor import format
4 #
5 # Copyright © 2015 Keith Packard <keithp@keithp.com>, GPL v3+
6 #
7
8 bool    mfg_part = false;
9 string[*]       vendors;
10 string          vendor_list;
11 string[*]       not_vendors;
12 string          not_vendor_list;
13 string  input_name;
14 string  output_name;
15 string  program;
16 int     argi;
17 int     lineno = 0;
18
19 void fatal(string format, poly args ...)
20 {
21         File::fprintf(stderr, format, args...);
22         exit(1);
23 }
24
25 string[*] read_line(file f) {
26         lineno++;
27         string  line = fgets(f);
28
29         string[*] elts = String::parse_csv(line);
30         for (int i = 0; i < dim(elts); i++)
31                 if (String::length(elts[i]) > 0 && elts[i][0] == '"')
32                         elts[i] = String::dequote(elts[i]);
33         return elts;
34 }
35
36 string[*] header;
37
38 string[*] required_elements = {
39         "quantity",
40         "vendor",
41         "vendor_part_number",
42         "mfg_part_number",
43         "device",
44         "value",
45         "refdes",
46         "loadstatus"
47 };
48
49 bool has_header_member(string member) {
50         for (int i = 0; i < dim(header); i++)
51                 if (header[i] == member)
52                         return true;
53         return false;
54 }
55
56 bool has_vendor(string[*] vendors, string vendor) {
57         for (int i = 0; i < dim(vendors); i++)
58                 if (vendors[i] == vendor)
59                         return true;
60         return false;
61 }
62
63 void read_header(file f) {
64         header = read_line(f);
65
66         for (int i = 0; i < dim(required_elements); i++)
67                 if (!has_header_member(required_elements[i]))
68                         fatal("Missing header element \"%s\"\n", required_elements[i]);
69 }
70
71 string[string] read_entry(file f) {
72         string[*]       elements = read_line(f);
73         string[string]  entry = {};
74
75         if (dim(header) != dim(elements))
76                 fatal("line %d: has %d instead of %d elements (%V)\n",
77                       lineno, dim(elements), dim(header), elements);
78
79         for (int i = 0; i < dim(header); i++) {
80                 if (elements[i] == "")
81                         elements[i] = "unknown";
82                 entry[header[i]] = elements[i];
83         }
84         return entry;
85 }
86
87 string part_number(string[string] entry)
88 {
89         if (mfg_part)
90                 return entry["mfg_part_number"];
91         else
92                 return entry["vendor_part_number"];
93 }
94
95 string quoted(string v)
96 {
97         if (String::index(v, "\"") >= 0 || String::index(v, ",") >= 0) {
98                 if (String::index(v, "\"") >= 0) {
99                         string ret = "\"";
100                         for (int i = 0; i < String::length(v); i++) {
101                                 if (v[i] == '"')
102                                         ret = ret + "\"";
103                                 ret = ret + String::new(v[i]);
104                         }
105                         ret = ret + "\"";
106                         return ret;
107                 } else {
108                         return "\"" + v + "\"";
109                 }
110         } else {
111                 return v;
112         }
113 }
114
115 void process_seeed(string[string] entry)
116 {
117         if (entry["loadstatus"] == "noload")
118                 return;
119
120         static bool start = true;
121         if (start) {
122                 printf("Part/Designator,Manufacturer Part Number/Seeed SKU, Quantity\n");
123                 start = false;
124         }
125
126         string[*] refdes = String::wordsplit(entry["refdes"], " \t");
127         if (dim(refdes) > 1)
128                 printf ("\"");
129         for (int i = 0; i < dim(refdes); i++) {
130                 printf("%s", refdes[i]);
131                 if (i < dim(refdes) - 1)
132                         printf (",");
133         }
134         if (dim(refdes) > 1)
135                 printf ("\"");
136         printf(",%s,%s\n", quoted(entry["mfg_part_number"]), entry["quantity"]);
137 }
138
139 void process_goldphoenix(string[string] entry)
140 {
141         int units = 1000;
142
143         if (entry["loadstatus"] == "noload")
144                 return;
145
146         static int item = 1;
147         static bool start = true;
148         if (start) {
149                 printf("#Item,Description,Designator,Package,Manufacturer,Manufacturer Part Number#,Supplier,Supplier Part #,QTY/BOARD,Order QTY,Unit Price, Subtotal \n");
150                 start = false;
151         }
152
153         string[*] refdes = String::wordsplit(entry["refdes"], " \t");
154         printf("%d,", item);
155
156         /* description */
157         printf("%s %s,",
158                quoted(entry["device"]),
159                quoted(entry["value"]));
160
161         /* designators */
162         if (dim(refdes) > 1)
163                 printf ("\"");
164         for (int i = 0; i < dim(refdes); i++) {
165                 printf("%s", refdes[i]);
166                 if (i < dim(refdes) - 1)
167                         printf (",");
168         }
169         if (dim(refdes) > 1)
170                 printf ("\"");
171
172         /* rest */
173         printf(",%s,%s,%s,%s,%s,%s,%d\n",
174                quoted(entry["footprint"]),
175                quoted(entry["mfg"]),
176                quoted(entry["mfg_part_number"]),
177                quoted(entry["vendor"]),
178                quoted(entry["vendor_part_number"]),
179                entry["quantity"],
180                dim(refdes) * units);
181         item++;
182 }
183
184 void process_digikey(string[string] entry)
185 {
186         if (entry["loadstatus"] == "noload")
187                 return;
188         printf("%s,%s,%s %s\n",
189                entry["quantity"],
190                quoted(part_number(entry)),
191                quoted(entry["device"]),
192                quoted(entry["value"]));
193 }
194
195 void process_mouser(string[string] entry)
196 {
197         if (entry["loadstatus"] == "noload")
198                 return;
199 /*      printf("%s|%s\n", part_number(entry), entry["quantity"]); */
200
201         static bool start = true;
202
203         if (start) {
204                 printf("Mouser Part Number,Manufacturer Part Number,Quantity\n");
205                 start = false;
206         }
207
208         printf("%s,%s,%s\n",
209                quoted(entry["vendor_part_number"]),
210                quoted(entry["mfg_part_number"]),
211                entry["quantity"]);
212 }
213
214 void process_other(string[string] entry) {
215         if (entry["loadstatus"] == "noload")
216                 return;
217         printf("%s,%s,%s,%s %s\n",
218                entry["vendor"],
219                entry["quantity"],
220                quoted(part_number(entry)),
221                quoted(entry["device"]),
222                entry["value"]);
223 }
224
225 void process_file(file f) {
226         read_header(f);
227         while (!File::end(f)) {
228                 string[string] entry = read_entry(f);
229                 string vendor = entry["vendor"];
230                 if (!is_uninit(&vendors) && has_vendor(vendors, "seeed")) {
231                         process_seeed(entry);
232                 } else if (!is_uninit(&vendors) && has_vendor(vendors, "goldphoenix")) {
233                         process_goldphoenix(entry);
234                 } else if ((!is_uninit(&vendors) && has_vendor(vendors, vendor)) ||
235                            (!is_uninit(&not_vendors) && !has_vendor(not_vendors, vendor))) {
236                         switch (entry["vendor"]) {
237                         case "digikey":
238                                 process_digikey(entry);
239                                 break;
240                         case "mouser":
241                                 process_mouser(entry);
242                                 break;
243                         default:
244                                 process_other(entry);
245                                 break;
246                         }
247                 }
248         }
249 }
250
251 ParseArgs::argdesc argd = {
252         .args = {
253                 { .var = { .arg_flag = &mfg_part },
254                   .abbr = 'm',
255                   .name = "mfg",
256                   .desc = "Display manufacturer part number"},
257                 { .var = { .arg_string = &vendor_list },
258                   .abbr = 'v',
259                   .name = "vendor",
260                   .expr_name = "vendors",
261                   .desc = "Vendors to match"},
262                 { .var = { .arg_string = &not_vendor_list },
263                   .abbr = 'n',
264                   .name = "not-vendor",
265                   .expr_name = "not-vendor",
266                   .desc = "Vendors to exclude"},
267         },
268         .unknown = &argi,
269 };
270
271 void main() {
272         ParseArgs::parseargs(&argd, &argv);
273         if (!is_uninit(&vendor_list))
274                 vendors = String::parse_csv(vendor_list);
275
276         if (!is_uninit(&not_vendor_list))
277                 not_vendors = String::parse_csv(not_vendor_list);
278
279         if (!is_uninit(&argi)) {
280                 for (int i = argi; i < dim(argv); i++)
281                         twixt(file f = File::open(argv[i], "r"); File::close(f))
282                                 process_file(f);
283         } else
284                 process_file(stdin);
285 }
286
287 main();